Merge pull request #3 from nim-lang/devel

pull #3
This commit is contained in:
Michael Voronin 2018-05-03 17:12:01 +03:00 • committed by GitHub
commit 5ea967d97a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
107 changed files with 1137 additions and 2841 deletions

View file

@ -1,4 +1,4 @@
## v0.X.X - XX/XX/2018 ## v0.19.X - XX/XX/2018
### Changes affecting backwards compatibility ### Changes affecting backwards compatibility
@ -11,9 +11,12 @@
to deal with! to deal with!
- Indexing into a ``cstring`` for the JS target is now mapped - Indexing into a ``cstring`` for the JS target is now mapped
to ``charCodeAt``. 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 at runtime. Use ``ref object`` with inheritance rather than ``object`` with
inheritance to prevent this issue. 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 #### Breaking changes in the standard library
@ -81,6 +84,14 @@
Imported exceptions can be raised and caught just like Nim exceptions. Imported exceptions can be raised and caught just like Nim exceptions.
More details in language manual. 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 ### Tool changes
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed - ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed

View file

@ -125,15 +125,15 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
of tyString, tySequence: of tyString, tySequence:
if skipTypes(n.typ, abstractInst).kind == tyVar and if skipTypes(n.typ, abstractInst).kind == tyVar and
not compileToCpp(p.module): 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: else:
result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)] result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)]
of tyArray: of tyArray:
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))] result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
of tyPtr, tyRef: of tyPtr, tyRef:
case lastSon(a.t).kind case lastSon(a.t).kind
of tyString, tySequence: 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: of tyArray:
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))] result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
else: else:
@ -143,7 +143,7 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} = proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[0], a) 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 = proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
var a: TLoc var a: TLoc

View file

@ -63,6 +63,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
of tyNil: of tyNil:
result = genNilStringLiteral(p.module, n.info) result = genNilStringLiteral(p.module, n.info)
of tyString: 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) result = genStringLiteral(p.module, n)
else: else:
if n.strVal.isNil: result = rope("NIM_NIL") 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))) rdCharLoc(idx), first, intLiteral(lastOrd(ty)))
of tySequence, tyString: of tySequence, tyString:
linefmt(p, cpsStmts, 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)) rdLoc(idx), rdLoc(arr), lenField(p))
else: discard else: discard
@ -905,13 +909,13 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if ty.kind in {tyRef, tyPtr}: if ty.kind in {tyRef, tyPtr}:
ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check:
if optBoundsCheck in p.options: 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, linefmt(p, cpsStmts,
"if ((NU)($1) > (NU)($2->$3)) #raiseIndexError();$n", "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
rdLoc(b), rdLoc(a), lenField(p)) rdLoc(b), rdLoc(a), lenField(p))
else: else:
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"if ((NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n",
rdLoc(b), rdLoc(a), lenField(p)) rdLoc(b), rdLoc(a), lenField(p))
if d.k == locNone: d.storage = OnHeap if d.k == locNone: d.storage = OnHeap
if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
@ -980,10 +984,10 @@ proc genEcho(p: BProc, n: PNode) =
var a: TLoc var a: TLoc
for it in n.sons: for it in n.sons:
if it.skipConv.kind == nkNilLit: if it.skipConv.kind == nkNilLit:
add(args, ", \"nil\"") add(args, ", \"\"")
else: else:
initLocExpr(p, it, a) initLocExpr(p, it, a)
addf(args, ", $1? ($1)->data:\"nil\"", [rdLoc(a)]) addf(args, ", $1? ($1)->data:\"\"", [rdLoc(a)])
p.module.includeHeader("<base/log.h>") p.module.includeHeader("<base/log.h>")
linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args) linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args)
else: else:
@ -1034,7 +1038,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}: if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}:
inc(L, len(e.sons[i + 1].strVal)) inc(L, len(e.sons[i + 1].strVal))
else: 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))) 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)) linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L))
add(p.s(cpsStmts), appends) 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}: if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}:
inc(L, len(e.sons[i + 2].strVal)) inc(L, len(e.sons[i + 2].strVal))
else: 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", add(appends, rfmt(p.module, "#appendString($1, $2);$n",
rdLoc(dest), rdLoc(a))) rdLoc(dest), rdLoc(a)))
linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n", 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 = (typeof seq) incrSeq(&seq->Sup, sizeof(x));
# seq->data[seq->len-1] = x; # seq->data[seq->len-1] = x;
let seqAppendPattern = if not p.module.compileToCpp: let seqAppendPattern = if not p.module.compileToCpp:
"$1 = ($2) #incrSeqV2(&($1)->Sup, sizeof($3));$n" "$1 = ($2) #incrSeqV3(&($1)->Sup, $3);$n"
else: else:
"$1 = ($2) #incrSeqV2($1, sizeof($3));$n" "$1 = ($2) #incrSeqV3($1, $3);$n"
var a, b, dest, tmpL: TLoc var a, b, dest, tmpL: TLoc
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) 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, [ lineCg(p, cpsStmts, seqAppendPattern, [
rdLoc(a), rdLoc(a),
getTypeDesc(p.module, e.sons[1].typ), getTypeDesc(p.module, e.sons[1].typ),
getTypeDesc(p.module, bt)]) genTypeInfo(p.module, seqType, e.info)])
#if bt != b.t: #if bt != b.t:
# echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
initLoc(dest, locExpr, e.sons[2], OnHeap) 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) putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
of tyString, tySequence: of tyString, tySequence:
putIntoDest(p, b, e, 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: of tyArray:
putIntoDest(p, b, e, putIntoDest(p, b, e,
"$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))], a.storage) "$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) initLocExpr(p, e.sons[2], b)
let t = skipTypes(e.sons[1].typ, {tyVar}) let t = skipTypes(e.sons[1].typ, {tyVar})
let setLenPattern = if not p.module.compileToCpp: 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: else:
"$1 = ($3) #setLengthSeq($1, sizeof($4), $2);$n" "$1 = ($3) #setLengthSeqV2($1, $4, $2);$n"
lineCg(p, cpsStmts, setLenPattern, [ lineCg(p, cpsStmts, setLenPattern, [
rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), 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) gcUsage(e)
proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = 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) = proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[0], a) 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) a.storage)
proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = 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 x: TLoc
var a = e.sons[1] var a = e.sons[1]
var b = e.sons[2] var b = e.sons[2]
if (a.kind == nkNilLit) or (b.kind == nkNilLit): if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "":
binaryExpr(p, e, d, "($1 == $2)")
elif (a.kind in {nkStrLit..nkTripleStrLit}) and (a.strVal == ""):
initLocExpr(p, e.sons[2], x) initLocExpr(p, e.sons[2], x)
putIntoDest(p, d, e, putIntoDest(p, d, e,
rfmt(nil, "(($1) && ($1)->$2 == 0)", rdLoc(x), lenField(p))) rfmt(nil, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p)))
elif (b.kind in {nkStrLit..nkTripleStrLit}) and (b.strVal == ""): elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "":
initLocExpr(p, e.sons[1], x) initLocExpr(p, e.sons[1], x)
putIntoDest(p, d, e, 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: else:
binaryExpr(p, e, d, "#eqStrings($1, $2)") binaryExpr(p, e, d, "#eqStrings($1, $2)")

View file

@ -54,6 +54,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
const const
HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" & HelpMessage = "Nim Compiler Version $1 [$2: $3]\n" &
"Compiled at $4 $5\n" &
"Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n" "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n"
const const
@ -68,7 +69,8 @@ const
proc getCommandLineDesc(): string = proc getCommandLineDesc(): string =
result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name, result = (HelpMessage % [VersionAsString, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]) & Usage CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
Usage
proc helpOnError(pass: TCmdLinePass) = proc helpOnError(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
@ -79,7 +81,8 @@ proc writeAdvancedUsage(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString, msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]) & AdvancedUsage, CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
AdvancedUsage,
{msgStdout}) {msgStdout})
msgQuit(0) msgQuit(0)
@ -87,7 +90,8 @@ proc writeFullhelp(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString, msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]) & Usage & AdvancedUsage, CPU[platform.hostCPU].name, CompileDate, CompileTime]) &
Usage & AdvancedUsage,
{msgStdout}) {msgStdout})
msgQuit(0) msgQuit(0)
@ -95,7 +99,7 @@ proc writeVersionInfo(pass: TCmdLinePass) =
if pass == passCmd1: if pass == passCmd1:
msgWriteln(`%`(HelpMessage, [VersionAsString, msgWriteln(`%`(HelpMessage, [VersionAsString,
platform.OS[platform.hostOS].name, platform.OS[platform.hostOS].name,
CPU[platform.hostCPU].name]), CPU[platform.hostCPU].name, CompileDate, CompileTime]),
{msgStdout}) {msgStdout})
const gitHash = gorge("git log -n 1 --format=%H").strip 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) if i < len(arg) and (arg[i] in {':', '='}): inc(i)
else: invalidCmdLineOption(pass, orig, info) else: invalidCmdLineOption(pass, orig, info)
if state == wHint: if state == wHint:
var x = findStr(msgs.HintsToStr, id) let x = findStr(msgs.HintsToStr, id)
if x >= 0: n = TNoteKind(x + ord(hintMin)) if x >= 0: n = TNoteKind(x + ord(hintMin))
else: localError(info, "unknown hint: " & id) else: localError(info, "unknown hint: " & id)
else: else:
var x = findStr(msgs.WarningsToStr, id) let x = findStr(msgs.WarningsToStr, id)
if x >= 0: n = TNoteKind(x + ord(warnMin)) if x >= 0: n = TNoteKind(x + ord(warnMin))
else: localError(info, "unknown warning: " & id) else: localError(info, "unknown warning: " & id)
case substr(arg, i).normalize case substr(arg, i).normalize
@ -499,6 +503,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
undefSymbol("nimOldNewlines") undefSymbol("nimOldNewlines")
else: else:
localError(info, errOnOrOffExpectedButXFound, arg) localError(info, errOnOrOffExpectedButXFound, arg)
of "laxstrings": processOnOffSwitch({optLaxStrings}, arg, pass, info)
of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info) of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info)
of "floatchecks": of "floatchecks":
processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info) processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info)

View file

@ -82,7 +82,6 @@ proc countDefinedSymbols*(): int =
proc initDefines*() = proc initDefines*() =
gSymbols = newStringTable(modeStyleInsensitive) gSymbols = newStringTable(modeStyleInsensitive)
defineSymbol("nimrod") # 'nimrod' is always defined
# for bootstrapping purposes and old code: # for bootstrapping purposes and old code:
defineSymbol("nimhygiene") defineSymbol("nimhygiene")
defineSymbol("niminheritable") defineSymbol("niminheritable")
@ -115,3 +114,6 @@ proc initDefines*() =
defineSymbol("nimHasNilChecks") defineSymbol("nimHasNilChecks")
defineSymbol("nimSymKind") defineSymbol("nimSymKind")
defineSymbol("nimVmEqIdent") defineSymbol("nimVmEqIdent")
defineSymbol("nimNoNil")
defineSymbol("nimNoZeroTerminator")
defineSymbol("nimNotNil")

View file

@ -42,7 +42,8 @@ proc newLine(p: var TTmplParser) =
proc scanPar(p: var TTmplParser, d: int) = proc scanPar(p: var TTmplParser, d: int) =
var i = d var i = d
while true: let hi = p.x.len - 1
while i <= hi:
case p.x[i] case p.x[i]
of '\0': break of '\0': break
of '(': inc(p.par) of '(': inc(p.par)

View file

@ -165,13 +165,12 @@ proc isKeyword*(kind: TTokType): bool =
template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion
proc isNimIdentifier*(s: string): bool = 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 i = 1
var sLen = s.len
while i < sLen: while i < sLen:
if s[i] == '_': if s[i] == '_': inc(i)
inc(i) if i < sLen and s[i] notin SymChars: return
if s[i] notin SymChars: return
inc(i) inc(i)
result = true result = true
@ -311,12 +310,12 @@ template tokenEndPrevious(tok, pos) =
# We need to parse the largest uint literal without overflow checks # We need to parse the largest uint literal without overflow checks
proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int = proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
var i = start var i = start
if s[i] in {'0'..'9'}: if i < s.len and s[i] in {'0'..'9'}:
b = 0 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')) b = b * 10 + (ord(s[i]) - ord('0'))
inc(i) 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 result = i - start
{.pop.} # overflowChecks {.pop.} # overflowChecks

View file

@ -126,7 +126,7 @@ type
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported, warnUnknownSubstitutionX, warnLanguageXNotSupported,
warnFieldXNotSupported, warnCommentXIgnored, warnFieldXNotSupported, warnCommentXIgnored,
warnNilStatement, warnTypelessParam, warnTypelessParam,
warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent, warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
@ -400,7 +400,6 @@ const
warnLanguageXNotSupported: "language \'$1\' not supported", warnLanguageXNotSupported: "language \'$1\' not supported",
warnFieldXNotSupported: "field \'$1\' not supported", warnFieldXNotSupported: "field \'$1\' not supported",
warnCommentXIgnored: "comment \'$1\' ignored", 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'", warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
warnWriteToForeignHeap: "write to foreign heap", warnWriteToForeignHeap: "write to foreign heap",
@ -451,7 +450,7 @@ const
"SmallLshouldNotBeUsed", "UnknownMagic", "SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX", "RedefinitionOfLabel", "UnknownSubstitutionX",
"LanguageXNotSupported", "FieldXNotSupported", "LanguageXNotSupported", "FieldXNotSupported",
"CommentXIgnored", "NilStmt", "CommentXIgnored",
"TypelessParam", "UseBase", "WriteToForeignHeap", "TypelessParam", "UseBase", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent", "UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
@ -474,6 +473,10 @@ const
hintMin* = hintSuccess hintMin* = hintSuccess
hintMax* = high(TMsgKind) hintMax* = high(TMsgKind)
static:
doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
type type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind] TNoteKinds* = set[TNoteKind]

View file

@ -47,7 +47,7 @@ proc mainCommand =
compileProject(newModuleGraph(), newIdentCache()) compileProject(newModuleGraph(), newIdentCache())
pretty.overwriteFiles() pretty.overwriteFiles()
proc processCmdLine*(pass: TCmdLinePass, cmd: string) = proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) =
var p = parseopt.initOptParser(cmd) var p = parseopt.initOptParser(cmd)
var argsCount = 0 var argsCount = 0
gOnlyMainfile = true gOnlyMainfile = true
@ -76,16 +76,16 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
of "wholeproject": gOnlyMainfile = false of "wholeproject": gOnlyMainfile = false
of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error
else: else:
processSwitch(pass, p) processSwitch(pass, p, config)
of cmdArgument: of cmdArgument:
options.gProjectName = unixToNativePath(p.key) options.gProjectName = unixToNativePath(p.key)
# if processArgument(pass, p, argsCount): break # if processArgument(pass, p, argsCount): break
proc handleCmdLine() = proc handleCmdLine(config: ConfigRef) =
if paramCount() == 0: if paramCount() == 0:
stdout.writeLine(Usage) stdout.writeLine(Usage)
else: else:
processCmdLine(passCmd1, "") processCmdLine(passCmd1, "", config)
if gProjectName != "": if gProjectName != "":
try: try:
gProjectFull = canonicalizePath(gProjectName) gProjectFull = canonicalizePath(gProjectName)
@ -96,11 +96,11 @@ proc handleCmdLine() =
gProjectName = p.name gProjectName = p.name
else: else:
gProjectPath = getCurrentDir() 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 # now process command line arguments again, because some options in the
# command line can overwite the config file's settings # command line can overwite the config file's settings
extccomp.initVars() extccomp.initVars()
processCmdLine(passCmd2, "") processCmdLine(passCmd2, "", config)
mainCommand() mainCommand()
when compileOption("gc", "v2") or compileOption("gc", "refc"): when compileOption("gc", "v2") or compileOption("gc", "refc"):
@ -108,4 +108,4 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"):
condsyms.initDefines() condsyms.initDefines()
defineSymbol "nimfix" defineSymbol "nimfix"
handleCmdline() handleCmdline newConfigRef()

View file

@ -37,7 +37,8 @@ type # please make sure we have under 32 options
# evaluation # evaluation
optPatterns, # en/disable pattern matching optPatterns, # en/disable pattern matching
optMemTracker, optMemTracker,
optHotCodeReloading optHotCodeReloading,
optLaxStrings
TOptions* = set[TOption] TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible** TGlobalOption* = enum # **keep binary compatible**
@ -108,7 +109,8 @@ type
dotOperators, dotOperators,
callOperator, callOperator,
parallel, parallel,
destructor destructor,
notnil
ConfigRef* = ref object ## eventually all global configuration should be moved here ConfigRef* = ref object ## eventually all global configuration should be moved here
cppDefines*: HashSet[string] cppDefines*: HashSet[string]

View file

@ -175,10 +175,11 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) =
proc putComment(g: var TSrcGen, s: string) = proc putComment(g: var TSrcGen, s: string) =
if s.isNil: return if s.isNil: return
var i = 0 var i = 0
let hi = len(s) - 1
var isCode = (len(s) >= 2) and (s[1] != ' ') var isCode = (len(s) >= 2) and (s[1] != ' ')
var ind = g.lineLen var ind = g.lineLen
var com = "## " var com = "## "
while true: while i <= hi:
case s[i] case s[i]
of '\0': of '\0':
break break
@ -201,12 +202,12 @@ proc putComment(g: var TSrcGen, s: string) =
# gets too long: # gets too long:
# compute length of the following word: # compute length of the following word:
var j = i 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): if not isCode and (g.lineLen + (j - i) > MaxLineLen):
put(g, tkComment, com) put(g, tkComment, com)
optNL(g, ind) optNL(g, ind)
com = "## " com = "## "
while s[i] > ' ': while i <= hi and s[i] > ' ':
add(com, s[i]) add(com, s[i])
inc(i) inc(i)
put(g, tkComment, com) put(g, tkComment, com)
@ -215,8 +216,9 @@ proc putComment(g: var TSrcGen, s: string) =
proc maxLineLength(s: string): int = proc maxLineLength(s: string): int =
if s.isNil: return 0 if s.isNil: return 0
var i = 0 var i = 0
let hi = len(s) - 1
var lineLen = 0 var lineLen = 0
while true: while i <= hi:
case s[i] case s[i]
of '\0': of '\0':
break break
@ -235,7 +237,7 @@ proc maxLineLength(s: string): int =
proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) = proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
var i = 0 var i = 0
var hi = len(s) - 1 let hi = len(s) - 1
var str = "" var str = ""
while i <= hi: while i <= hi:
case s[i] case s[i]
@ -1064,6 +1066,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
if n.len > 1: if n.len > 1:
let opr = if n[0].kind == nkIdent: n[0].ident let opr = if n[0].kind == nkIdent: n[0].ident
elif n[0].kind == nkSym: n[0].sym.name elif n[0].kind == nkSym: n[0].sym.name
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
else: nil else: nil
if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)): if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
put(g, tkSpaces, Space) put(g, tkSpaces, Space)

View file

@ -188,11 +188,11 @@ iterator leaves*(r: Rope): string =
var stack = @[r] var stack = @[r]
while stack.len > 0: while stack.len > 0:
var it = stack.pop var it = stack.pop
while isNil(it.data): while it.left != nil:
assert it.right != nil
stack.add(it.right) stack.add(it.right)
it = it.left it = it.left
assert(it != nil) assert(it != nil)
assert(it.data != nil)
yield it.data yield it.data
iterator items*(r: Rope): char = iterator items*(r: Rope): char =
@ -251,7 +251,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
while true: while true:
j = j * 10 + ord(frmt[i]) - ord('0') j = j * 10 + ord(frmt[i]) - ord('0')
inc(i) inc(i)
if frmt[i] notin {'0'..'9'}: break if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
num = j num = j
if j > high(args) + 1: if j > high(args) + 1:
errorHandler(rInvalidFormatStr, $(j)) errorHandler(rInvalidFormatStr, $(j))

View file

@ -153,8 +153,9 @@ proc commonType*(x, y: PType): PType =
if a.kind in {tyRef, tyPtr}: if a.kind in {tyRef, tyPtr}:
k = a.kind k = a.kind
if b.kind != a.kind: return x if b.kind != a.kind: return x
a = a.lastSon # bug #7601, array construction of ptr generic
b = b.lastSon a = a.lastSon.skipTypes({tyGenericInst})
b = b.lastSon.skipTypes({tyGenericInst})
if a.kind == tyObject and b.kind == tyObject: if a.kind == tyObject and b.kind == tyObject:
result = commonSuperclass(a, b) result = commonSuperclass(a, b)
# this will trigger an error later: # this will trigger an error later:

View file

@ -59,7 +59,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
filter: TSymKinds, filter: TSymKinds,
best, alt: var TCandidate, best, alt: var TCandidate,
errors: var CandidateErrors, errors: var CandidateErrors,
diagnosticsFlag = false) = diagnosticsFlag: bool,
errorsEnabled: bool) =
var o: TOverloadIter var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol) var sym = initOverloadIter(o, c, headSymbol)
var scope = o.lastOverloadScope 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))' # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
let counterInitial = c.currentScope.symbols.counter let counterInitial = c.currentScope.symbols.counter
var syms: seq[tuple[s: PSym, scope: int]] var syms: seq[tuple[s: PSym, scope: int]]
var noSyms = true
var nextSymIndex = 0 var nextSymIndex = 0
while sym != nil: while sym != nil:
if sym.kind in filter: if sym.kind in filter:
@ -102,7 +104,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
var cmp = cmpCandidates(best, z) var cmp = cmpCandidates(best, z)
if cmp < 0: best = z # x is better than the best so far if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far elif cmp == 0: alt = z # x is as good as the best so far
elif errors != nil or z.diagnostics != nil: elif errorsEnabled or z.diagnosticsEnabled:
errors.safeAdd(CandidateError( errors.safeAdd(CandidateError(
sym: sym, sym: sym,
unmatchedVarParam: int z.mutabilityProblem, 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. # before any further candidate init and compare. SLOW, but rare case.
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter, syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
best, alt, o, diagnosticsFlag) best, alt, o, diagnosticsFlag)
if syms == nil: noSyms = false
if noSyms:
sym = nextOverloadIter(o, c, headSymbol) sym = nextOverloadIter(o, c, headSymbol)
scope = o.lastOverloadScope scope = o.lastOverloadScope
elif nextSymIndex < syms.len: elif nextSymIndex < syms.len:
@ -206,7 +209,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
if errorOutputs == {}: if errorOutputs == {}:
# fail fast: # fail fast:
globalError(n.info, errTypeMismatch, "") globalError(n.info, errTypeMismatch, "")
if errors.isNil or errors.len == 0: if errors.len == 0:
localError(n.info, errExprXCannotBeCalled, n[0].renderTree) localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
return return
@ -227,7 +230,8 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
if symx.kind in routineKinds: if symx.kind in routineKinds:
errors.add(CandidateError(sym: symx, errors.add(CandidateError(sym: symx,
unmatchedVarParam: 0, firstMismatch: 0, unmatchedVarParam: 0, firstMismatch: 0,
diagnostics: nil)) diagnostics: nil,
enabled: false))
symx = nextOverloadIter(o, c, headSymbol) symx = nextOverloadIter(o, c, headSymbol)
if errors.len == 0: if errors.len == 0:
localError(n.info, "could not resolve: " & $n) localError(n.info, "could not resolve: " & $n)
@ -236,7 +240,8 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
proc resolveOverloads(c: PContext, n, orig: PNode, proc resolveOverloads(c: PContext, n, orig: PNode,
filter: TSymKinds, flags: TExprFlags, filter: TSymKinds, flags: TExprFlags,
errors: var CandidateErrors): TCandidate = errors: var CandidateErrors,
errorsEnabled: bool): TCandidate =
var initialBinding: PNode var initialBinding: PNode
var alt: TCandidate var alt: TCandidate
var f = n.sons[0] var f = n.sons[0]
@ -249,7 +254,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
template pickBest(headSymbol) = template pickBest(headSymbol) =
pickBestCandidate(c, headSymbol, n, orig, initialBinding, pickBestCandidate(c, headSymbol, n, orig, initialBinding,
filter, result, alt, errors, efExplain in flags) filter, result, alt, errors, efExplain in flags,
errorsEnabled)
pickBest(f) pickBest(f)
let overloadsState = result.state let overloadsState = result.state
@ -423,9 +429,8 @@ proc tryDeref(n: PNode): PNode =
proc semOverloadedCall(c: PContext, n, nOrig: PNode, proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds, flags: TExprFlags): PNode = filter: TSymKinds, flags: TExprFlags): PNode =
var errors: CandidateErrors = if efExplain in flags: @[] var errors: CandidateErrors = if efExplain in flags: @[] else: nil
else: nil var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
if r.state == csMatch: if r.state == csMatch:
# this may be triggered, when the explain pragma is used # this may be triggered, when the explain pragma is used
if errors.len > 0: if errors.len > 0:
@ -443,7 +448,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
# into sigmatch with hidden conversion produced there # into sigmatch with hidden conversion produced there
# #
n.sons[1] = n.sons[1].tryDeref 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) if r.state == csMatch: result = semResolvedCall(c, n, r)
else: else:
# get rid of the deref again for a better error message: # get rid of the deref again for a better error message:

View file

@ -63,7 +63,7 @@ type
# to the user. # to the user.
efWantStmt, efAllowStmt, efDetermineType, efExplain, efWantStmt, efAllowStmt, efDetermineType, efExplain,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck, efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo, efNoEvaluateGeneric, efInCall, efFromHlo
TExprFlags* = set[TExprFlag] TExprFlags* = set[TExprFlag]

View file

@ -51,7 +51,6 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
renderTree(result, {renderNoComments})) renderTree(result, {renderNoComments}))
result.typ = errorType(c) result.typ = errorType(c)
else: else:
if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = 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, localError(n.info, errExprXHasNoType,
renderTree(result, {renderNoComments})) renderTree(result, {renderNoComments}))
result.typ = errorType(c) result.typ = errorType(c)
else:
semProcvarCheck(c, result)
proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
result = symChoice(c, n, s, scClosed) result = symChoice(c, n, s, scClosed)
@ -308,7 +305,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
maybeLiftType(t2, c, n.info) maybeLiftType(t2, c, n.info)
var m: TCandidate var m: TCandidate
initCandidate(c, m, t2) 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 let match = typeRel(m, t2, t1) >= isSubtype # isNone
result = newIntNode(nkIntLit, ord(match)) 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 # we need to recurse explicitly here as converters can create nested
# calls and then they wouldn't be analysed otherwise # calls and then they wouldn't be analysed otherwise
analyseIfAddressTakenInCall(c, n.sons[i]) analyseIfAddressTakenInCall(c, n.sons[i])
semProcvarCheck(c, n.sons[i])
if i < sonsLen(t) and if i < sonsLen(t) and
skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar:
if n.sons[i].kind != nkHiddenAddr: if n.sons[i].kind != nkHiddenAddr:
@ -1245,7 +1243,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
checkMinSonsLen(n, 2) checkMinSonsLen(n, 2)
# make sure we don't evaluate generic macros/templates # make sure we don't evaluate generic macros/templates
n.sons[0] = semExprWithType(c, n.sons[0], n.sons[0] = semExprWithType(c, n.sons[0],
{efNoProcvarCheck, efNoEvaluateGeneric}) {efNoEvaluateGeneric})
let arr = skipTypes(n.sons[0].typ, {tyGenericInst, let arr = skipTypes(n.sons[0].typ, {tyGenericInst,
tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
case arr.kind case arr.kind

View file

@ -13,7 +13,7 @@
import import
strutils, options, ast, astalgo, trees, treetab, nimsets, times, strutils, options, ast, astalgo, trees, treetab, nimsets, times,
nversion, platform, math, msgs, os, condsyms, idents, renderer, types, nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
commands, magicsys, saturate commands, magicsys
proc getConstExpr*(m: PSym, n: PNode): PNode proc getConstExpr*(m: PSym, n: PNode): PNode
# evaluates the constant expression or returns nil if it is no constant # 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 newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode
proc newStrNodeT*(strVal: string, 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 # implementation
proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode = 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 mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
of mToFloat, mToBiggestFloat: of mToFloat, mToBiggestFloat:
result = newFloatNodeT(toFloat(int(getInt(a))), n) result = newFloatNodeT(toFloat(int(getInt(a))), n)
# XXX: Hides overflow/underflow
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n) of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n) of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
of mAbsI: of mAbsI: result = foldAbs(getInt(a), n)
if getInt(a) >= 0: result = a
else: result = newIntNodeT(- getInt(a), n)
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64: of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
# byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64 # 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) result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n)
of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n) of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n) of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n)
of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n) of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n)
of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n) of mUnaryLt: result = foldSub(getOrdValue(a), 1, n)
of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n) of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n)
of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n) of mPred: result = foldSub(getOrdValue(a), getInt(b), n)
of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n) of mAddI: result = foldAdd(getInt(a), getInt(b), n)
of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n) of mSubI: result = foldSub(getInt(a), getInt(b), n)
of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n) of mMulI: result = foldMul(getInt(a), getInt(b), n)
of mMinI: of mMinI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n) if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
else: result = newIntNodeT(getInt(a), 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: of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shr`(getInt(a), getInt(b)), n) result = newIntNodeT(`shr`(getInt(a), getInt(b)), n)
else: internalError(n.info, "constant folding for shr") else: internalError(n.info, "constant folding for shr")
of mDivI: of mDivI: result = foldDiv(getInt(a), getInt(b), n)
let y = getInt(b) of mModI: result = foldMod(getInt(a), getInt(b), n)
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 mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n) of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
of mSubF64: 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) 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 mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n)
of mSubU: 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 mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n)
of mModU: of mModU: result = foldModU(getInt(a), getInt(b), n)
let y = getInt(b) of mDivU: result = foldDivU(getInt(a), getInt(b), n)
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 mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n) of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n)
of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n) of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n)
of mLtSet: of mLtSet:

View file

@ -71,37 +71,12 @@ proc toCover(t: PType): BiggestInt =
else: else:
result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc})) 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 semProc(c: PContext, n: PNode): PNode
proc semExprBranch(c: PContext, n: PNode): PNode = proc semExprBranch(c: PContext, n: PNode): PNode =
result = semExpr(c, n) result = semExpr(c, n)
if result.typ != nil: if result.typ != nil:
# XXX tyGenericInst here? # XXX tyGenericInst here?
semProcvarCheck(c, result)
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
proc semExprBranchScope(c: PContext, n: PNode): PNode = proc semExprBranchScope(c: PContext, n: PNode): PNode =
@ -132,20 +107,13 @@ proc fixNilType(n: PNode) =
proc discardCheck(c: PContext, result: PNode) = proc discardCheck(c: PContext, result: PNode) =
if c.matchedConcept != nil: return if c.matchedConcept != nil: return
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}: if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
if result.kind == nkNilLit: if implicitlyDiscardable(result):
result.typ = nil
message(result.info, warnNilStatement)
elif implicitlyDiscardable(result):
var n = result var n = result
result.typ = nil result.typ = nil
while n.kind in skipForDiscardable: while n.kind in skipForDiscardable:
n = n.lastSon n = n.lastSon
n.typ = nil n.typ = nil
elif result.typ.kind != tyError and gCmd != cmdInteractive: elif result.typ.kind != tyError and gCmd != cmdInteractive:
if result.typ.kind == tyNil:
fixNilType(result)
message(result.info, warnNilStatement)
else:
var n = result var n = result
while n.kind in skipForDiscardable: n = n.lastSon while n.kind in skipForDiscardable: n = n.lastSon
var s = "expression '" & $n & "' is of type '" & var s = "expression '" & $n & "' is of type '" &
@ -384,7 +352,7 @@ proc checkNilable(v: PSym) =
include semasgn 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 L = identDefs.len
let value = identDefs[L-1] let value = identDefs[L-1]
if result.kind == nkStmtList: if result.kind == nkStmtList:
@ -525,7 +493,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
localError(a.info, errXExpected, "tuple") localError(a.info, errXExpected, "tuple")
elif length-2 != sonsLen(tup): elif length-2 != sonsLen(tup):
localError(a.info, errWrongNumberOfVariables) localError(a.info, errWrongNumberOfVariables)
else:
b = newNodeI(nkVarTuple, a.info) b = newNodeI(nkVarTuple, a.info)
newSons(b, length) newSons(b, length)
b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
@ -572,7 +539,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addToVarSection(c, result, n, b) addToVarSection(c, result, n, b)
else: else:
if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j] 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) b.sons[j] = newSymNode(v)
checkNilable(v) checkNilable(v)
if sfCompileTime in v.flags: hasCompileTime = true 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 sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
if s.name.s[0] in {'.', '('}: if s.name.s[0] in {'.', '('}:
if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}: 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: 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: if n.sons[bodyPos].kind != nkEmpty:
# for DLL generation it is annoying to check for sfImportc! # 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) incl(s.typ.flags, tfCapturesEnv)
else: else:
s.typ.callConv = ccInline 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: if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone:
localError(n.info, errImplOfXexpected, s.name.s) 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) evalStaticStmt(c.module, c.cache, a, c.p.owner)
result = newNodeI(nkDiscardStmt, n.info, 1) result = newNodeI(nkDiscardStmt, n.info, 1)
result.sons[0] = emptyNode 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 = proc usesResult(n: PNode): bool =
# nkStmtList(expr) properly propagates the void context, # nkStmtList(expr) properly propagates the void context,
@ -1841,40 +1799,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
# nkNilLit, nkEmpty}: # nkNilLit, nkEmpty}:
# dec last # dec last
for i in countup(0, length - 1): 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) var expr = semExpr(c, n.sons[i], flags)
n.sons[i] = expr n.sons[i] = expr
if c.matchedConcept != nil and expr.typ != nil and if c.matchedConcept != nil and expr.typ != nil and
@ -1908,7 +1832,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.typ = n.sons[i].typ n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr if not isEmptyType(n.typ): n.kind = nkStmtListExpr
if n.sons[i].kind in LastBlockStmts or 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: 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): for j in countup(i + 1, length - 1):
case n.sons[j].kind case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr, of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
@ -1930,15 +1855,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
not (result.comment[0] == '#' and result.comment[1] == '#'): not (result.comment[0] == '#' and result.comment[1] == '#'):
# it is an old-style comment statement: we replace it with 'discard ""': # it is an old-style comment statement: we replace it with 'discard ""':
prettybase.replaceComment(result.info) 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 = proc semStmt(c: PContext, n: PNode): PNode =
# now: simply an alias: # now: simply an alias:

View file

@ -1389,6 +1389,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
n.sons[2].kind == nkNilLit: n.sons[2].kind == nkNilLit:
result = freshType(result, prev) result = freshType(result, prev)
result.flags.incl(tfNotNil) result.flags.incl(tfNotNil)
if notnil notin c.features:
localError(n.info, "enable the 'not nil' annotation with {.experimental: \"notnil\".}")
else: else:
localError(n.info, errGenerated, "invalid type") localError(n.info, errGenerated, "invalid type")
of 2: of 2:

View file

@ -26,6 +26,7 @@ type
sym*: PSym sym*: PSym
unmatchedVarParam*, firstMismatch*: int unmatchedVarParam*, firstMismatch*: int
diagnostics*: seq[string] diagnostics*: seq[string]
enabled*: bool
CandidateErrors* = seq[CandidateError] CandidateErrors* = seq[CandidateError]
@ -60,7 +61,8 @@ type
# matching. they will be reset if the matching # matching. they will be reset if the matching
# is not successful. may replace the bindings # is not successful. may replace the bindings
# table in the future. # 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 # will collect extra diagnostics that will be
# displayed to the user. # displayed to the user.
# triggered when overload resolution fails # triggered when overload resolution fails
@ -70,6 +72,7 @@ type
inheritancePenalty: int # to prefer closest father object type inheritancePenalty: int # to prefer closest father object type
firstMismatch*: int # position of the first type mismatch for firstMismatch*: int # position of the first type mismatch for
# better error messages # better error messages
diagnosticsEnabled*: bool
TTypeRelFlag* = enum TTypeRelFlag* = enum
trDontBind trDontBind
@ -124,7 +127,8 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
idTablePut(c.bindings, key, val.skipIntLit) idTablePut(c.bindings, key, val.skipIntLit)
proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, 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) initCandidateAux(ctx, c, callee.typ)
c.calleeSym = callee c.calleeSym = callee
if callee.kind in skProcKinds and calleeScope == -1: if callee.kind in skProcKinds and calleeScope == -1:
@ -139,7 +143,8 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
c.calleeScope = 1 c.calleeScope = 1
else: else:
c.calleeScope = calleeScope c.calleeScope = calleeScope
c.diagnostics = if diagnostics: @[] else: nil c.diagnostics = if diagnosticsEnabled: @[] else: nil
c.diagnosticsEnabled = diagnosticsEnabled
c.magic = c.calleeSym.magic c.magic = c.calleeSym.magic
initIdTable(c.bindings) initIdTable(c.bindings)
if binding != nil and callee.kind in routineKinds: if binding != nil and callee.kind in routineKinds:
@ -717,7 +722,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
diagnostics: seq[string] diagnostics: seq[string]
errorPrefix: string errorPrefix: string
flags: TExprFlags = {} flags: TExprFlags = {}
collectDiagnostics = m.diagnostics != nil or collectDiagnostics = m.diagnosticsEnabled or
sfExplain in typeClass.sym.flags sfExplain in typeClass.sym.flags
if collectDiagnostics: if collectDiagnostics:
@ -736,7 +741,9 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
if collectDiagnostics: if collectDiagnostics:
writelnHook = oldWriteHook 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 if checkedBody == nil: return nil
@ -1388,7 +1395,12 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
var aAsObject = roota.lastSon var aAsObject = roota.lastSon
if fKind in {tyRef, tyPtr} and aAsObject.kind == fKind: 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 aAsObject = aAsObject.base
if aAsObject.kind == tyObject: if aAsObject.kind == tyObject:

View file

@ -51,15 +51,15 @@ proc parseTopLevelStmt*(p: var TParsers): PNode =
result = ast.emptyNode result = ast.emptyNode
proc utf8Bom(s: string): int = 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 result = 3
else: else:
result = 0 result = 0
proc containsShebang(s: string, i: int): bool = 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 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] == '/' result = s[j] == '/'
proc parsePipe(filename: string, inputStream: PLLStream; cache: IdentCache): PNode = 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) discard llStreamReadLine(s, line)
i = 0 i = 0
inc linenumber inc linenumber
if line[i] == '#' and line[i+1] == '?': if i+1 < line.len and line[i] == '#' and line[i+1] == '?':
inc(i, 2) inc(i, 2)
while line[i] in Whitespace: inc(i) while i < line.len and line[i] in Whitespace: inc(i)
var q: TParser var q: TParser
parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache) parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache)
result = parser.parseAll(q) result = parser.parseAll(q)

View file

@ -612,13 +612,13 @@ proc firstOrd*(t: PType): BiggestInt =
else: else:
assert(t.n.sons[0].kind == nkSym) assert(t.n.sons[0].kind == nkSym)
result = t.n.sons[0].sym.position result = t.n.sons[0].sym.position
of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias: of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic:
result = firstOrd(lastSon(t)) result = firstOrd(lastSon(t))
of tyOrdinal: of tyOrdinal:
if t.len > 0: result = firstOrd(lastSon(t)) 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: else:
internalError("invalid kind for first(" & $t.kind & ')') internalError("invalid kind for firstOrd(" & $t.kind & ')')
result = 0 result = 0
proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt = proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
@ -651,14 +651,14 @@ proc lastOrd*(t: PType; fixedUnsigned = false): BiggestInt =
of tyEnum: of tyEnum:
assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym) assert(t.n.sons[sonsLen(t.n) - 1].kind == nkSym)
result = t.n.sons[sonsLen(t.n) - 1].sym.position 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)) result = lastOrd(lastSon(t))
of tyProxy: result = 0 of tyProxy: result = 0
of tyOrdinal: of tyOrdinal:
if t.len > 0: result = lastOrd(lastSon(t)) 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: else:
internalError("invalid kind for last(" & $t.kind & ')') internalError("invalid kind for lastOrd(" & $t.kind & ')')
result = 0 result = 0
proc lengthOrd*(t: PType): BiggestInt = proc lengthOrd*(t: PType): BiggestInt =

View file

@ -208,7 +208,12 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
result.add mapTypeToAst(t.sons[0], info) result.add mapTypeToAst(t.sons[0], info)
else: else:
result = mapTypeToBracket("ref", mRef, t, info) 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 tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info)
of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info) of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info)
of tySequence: result = mapTypeToBracket("seq", mSeq, t, info) of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)

View file

@ -1553,6 +1553,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
result = newNodeIT(nkFloatLit, info, t) result = newNodeIT(nkFloatLit, info, t)
of tyCString, tyString: of tyCString, tyString:
result = newNodeIT(nkStrLit, info, t) result = newNodeIT(nkStrLit, info, t)
result.strVal = ""
of tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr, of tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr,
tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil: tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil:
result = newNodeIT(nkNilLit, info, t) result = newNodeIT(nkNilLit, info, t)

View file

@ -66,6 +66,8 @@ Advanced options:
--excessiveStackTrace:on|off --excessiveStackTrace:on|off
stack traces use full file paths stack traces use full file paths
--oldNewlines:on|off turn on|off the old behaviour of "\n" --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 --skipCfg do not read the general configuration file
--skipUserCfg do not read the user's configuration file --skipUserCfg do not read the user's configuration file
--skipParentCfg do not read the parent dirs' configuration files --skipParentCfg do not read the parent dirs' configuration files

View file

@ -1471,7 +1471,7 @@ mysterious crashes.
**Note**: The example only works because the memory is initialized to zero **Note**: The example only works because the memory is initialized to zero
(``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to (``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. details like this when mixing garbage collected data with unmanaged memory.
.. XXX finalizers for traced objects .. XXX finalizers for traced objects
@ -2512,8 +2512,8 @@ char '\\0'
bool false bool false
ref or pointer type nil ref or pointer type nil
procedural type nil procedural type nil
sequence nil (*not* ``@[]``) sequence ``@[]``
string nil (*not* "") string ``""``
tuple[x: A, y: B, ...] (default(A), default(B), ...) tuple[x: A, y: B, ...] (default(A), default(B), ...)
(analogous for objects) (analogous for objects)
array[0..., T] [default(T), ...] array[0..., T] [default(T), ...]
@ -4270,7 +4270,7 @@ therefore very useful for type specialization within generic code:
Table[Key, Value] = object Table[Key, Value] = object
keys: seq[Key] keys: seq[Key]
values: seq[Value] 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] 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``: Custom pragmas are defined using templates annotated with pragma ``pragma``:
.. code-block:: nim .. code-block:: nim
template dbTable(name: string, table_space: string = nil) {.pragma.} template dbTable(name: string, table_space: string = "") {.pragma.}
template dbKey(name: string = nil, primary_key: bool = false) {.pragma.} template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
template dbForeignKey(t: typedesc) {.pragma.} template dbForeignKey(t: typedesc) {.pragma.}
template dbIgnore {.pragma.} template dbIgnore {.pragma.}

View file

@ -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 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`` length field. A string's length can be retrieved with the builtin ``len``
procedure; the length never counts the terminating zero. Accessing the procedure; the length never counts the terminating zero. Accessing the
terminating zero is not an error and often leads to simpler code: terminating zero is an error, it only exists so that a Nim string can be converted
to a ``cstring`` without doing a copy.
.. code-block:: nim
if s[i] == 'a' and s[i+1] == 'b':
# no need to check whether ``i < len(s)``!
...
The assignment operator for strings copies the string. You can use the ``&`` The assignment operator for strings copies the string. You can use the ``&``
operator to concatenate strings and ``add`` to append to a string. 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 a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of
``s``, not the i-th *unichar*. ``s``, not the i-th *unichar*.
String variables are initialized with a special value, called ``nil``. However, A string variable is initialized with the empty string ``""``.
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.
Integers Integers
@ -1309,11 +1301,7 @@ Example:
x: seq[int] # a reference to a sequence of integers 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 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 Sequence variables are initialized with ``@[]``.
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.
The ``for`` statement can be used with one or two variables when used with a 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 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 .. code-block:: nim
:test: "nim c $1" :test: "nim c $1"
var 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 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 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("Banana") # sequence 'fruits' is dynamically expandable during runtime
fruits.add("Mango") fruits.add("Mango")
@ -1691,7 +1677,7 @@ rules apply:
write(stdout, x(3)) # no error: A.x is called write(stdout, x(3)) # no error: A.x is called
write(stdout, x("")) # no error: B.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? write(stdout, x(3)) # ambiguous: which `x` is to call?

View file

@ -107,7 +107,7 @@ proc executeCgi(server: var TServer, client: Socket, path, query: string,
dataAvail = recvLine(client, buf) dataAvail = recvLine(client, buf)
if buf.len == 0: if buf.len == 0:
break break
var L = toLower(buf) var L = toLowerAscii(buf)
if L.startsWith("content-length:"): if L.startsWith("content-length:"):
var i = len("content-length:") var i = len("content-length:")
while L[i] in Whitespace: inc(i) while L[i] in Whitespace: inc(i)
@ -205,7 +205,7 @@ proc acceptRequest(server: var TServer, client: Socket) =
client.close() client.close()
else: else:
when defined(Windows): when defined(Windows):
var ext = splitFile(path).ext.toLower var ext = splitFile(path).ext.toLowerAscii
if ext == ".exe" or ext == ".cgi": if ext == ".exe" or ext == ".cgi":
# XXX: extract interpreter information here? # XXX: extract interpreter information here?
cgi = true cgi = true

View file

@ -10,7 +10,7 @@
from pcre import nil from pcre import nil
import nre.private.util import nre.private.util
import tables import tables
from strutils import toLower, `%` from strutils import `%`
from math import ceil from math import ceil
import options import options
from unicode import runeLenAt from unicode import runeLenAt
@ -645,7 +645,6 @@ template replaceImpl(str: string, pattern: Regex,
let bounds = match.matchBounds let bounds = match.matchBounds
result.add(str.substr(lastIdx, bounds.a - 1)) result.add(str.substr(lastIdx, bounds.a - 1))
let nextVal = replacement let nextVal = replacement
assert(nextVal != nil)
result.add(nextVal) result.add(nextVal)
lastIdx = bounds.b + 1 lastIdx = bounds.b + 1

View file

@ -774,7 +774,7 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) =
Digits + Letters + WhiteSpace) Digits + Letters + WhiteSpace)
let let
arg = getArgument(n) arg = getArgument(n)
isObject = arg.toLower().endsWith(".svg") isObject = arg.toLowerAscii().endsWith(".svg")
var var
options = "" options = ""
content = "" content = ""

View file

@ -177,7 +177,7 @@ proc fail*[T](future: Future[T], error: ref Exception) =
if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error) if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error)
future.callbacks.call() future.callbacks.call()
proc clearCallbacks(future: FutureBase) = proc clearCallbacks*(future: FutureBase) =
future.callbacks.function = nil future.callbacks.function = nil
future.callbacks.next = nil future.callbacks.next = nil

View file

@ -201,7 +201,7 @@ when defineSsl:
flags: set[SocketFlag]) {.async.} = flags: set[SocketFlag]) {.async.} =
let len = bioCtrlPending(socket.bioOut) let len = bioCtrlPending(socket.bioOut)
if len > 0: if len > 0:
var data = newStringOfCap(len) var data = newString(len)
let read = bioRead(socket.bioOut, addr data[0], len) let read = bioRead(socket.bioOut, addr data[0], len)
assert read != 0 assert read != 0
if read < 0: if read < 0:

View file

@ -134,7 +134,7 @@ proc decode*(s: string): string =
i = 0 i = 0
r = 0 r = 0
while true: 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: if i < s.len-3:
let let
a = s[i].decodeByte a = s[i].decodeByte

View file

@ -97,11 +97,10 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
var name = "" var name = ""
var value = "" var value = ""
# decode everything in one pass: # decode everything in one pass:
while data[i] != '\0': while i < data.len:
setLen(name, 0) # reuse memory setLen(name, 0) # reuse memory
while true: while i < data.len:
case data[i] case data[i]
of '\0': break
of '%': of '%':
var x = 0 var x = 0
handleHexChar(data[i+1], x) handleHexChar(data[i+1], x)
@ -112,13 +111,14 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
of '=', '&': break of '=', '&': break
else: add(name, data[i]) else: add(name, data[i])
inc(i) inc(i)
if data[i] != '=': cgiError("'=' expected") if i >= data.len or data[i] != '=': cgiError("'=' expected")
inc(i) # skip '=' inc(i) # skip '='
setLen(value, 0) # reuse memory setLen(value, 0) # reuse memory
while true: while i < data.len:
case data[i] case data[i]
of '%': of '%':
var x = 0 var x = 0
if i+2 < data.len:
handleHexChar(data[i+1], x) handleHexChar(data[i+1], x)
handleHexChar(data[i+2], x) handleHexChar(data[i+2], x)
inc(i, 2) inc(i, 2)
@ -128,8 +128,8 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] =
else: add(value, data[i]) else: add(value, data[i])
inc(i) inc(i)
yield (name.TaintedString, value.TaintedString) yield (name.TaintedString, value.TaintedString)
if i < data.len:
if data[i] == '&': inc(i) if data[i] == '&': inc(i)
elif data[i] == '\0': break
else: cgiError("'&' expected") else: cgiError("'&' expected")
iterator decodeData*(allowedMethods: set[RequestMethod] = iterator decodeData*(allowedMethods: set[RequestMethod] =

View file

@ -74,18 +74,19 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] =
var newByte = 0 var newByte = 0
block blockX: block blockX:
while newbyte < key.len: while newbyte < key.len:
if it.key[newbyte] != key[newbyte]: let ch = if newbyte < it.key.len: it.key[newbyte] else: '\0'
newotherbits = it.key[newbyte].ord xor key[newbyte].ord if ch != key[newbyte]:
newotherbits = ch.ord xor key[newbyte].ord
break blockX break blockX
inc newbyte inc newbyte
if it.key[newbyte] != '\0': if newbyte < it.key.len:
newotherbits = it.key[newbyte].ord newotherbits = it.key[newbyte].ord
else: else:
return it return it
while (newOtherBits and (newOtherBits-1)) != 0: while (newOtherBits and (newOtherBits-1)) != 0:
newOtherBits = newOtherBits and (newOtherBits-1) newOtherBits = newOtherBits and (newOtherBits-1)
newOtherBits = newOtherBits xor 255 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 let dir = (1 + (ord(ch) or newOtherBits)) shr 8
var inner: Node[T] var inner: Node[T]

View file

@ -25,16 +25,16 @@ proc parseCookies*(s: string): StringTableRef =
result = newStringTable(modeCaseInsensitive) result = newStringTable(modeCaseInsensitive)
var i = 0 var i = 0
while true: 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 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 var keyend = i-1
if s[i] == '\0': break if i >= s.len: break
inc(i) # skip '=' inc(i) # skip '='
var valstart = i 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) result[substr(s, keystart, keyend)] = substr(s, valstart, i-1)
if s[i] == '\0': break if i >= s.len: break
inc(i) # skip ';' inc(i) # skip ';'
proc setCookie*(key, value: string, domain = "", path = "", proc setCookie*(key, value: string, domain = "", path = "",

View file

@ -36,7 +36,7 @@ when defined(windows):
while i < a.len and j < b.len: while i < a.len and j < b.len:
if a[i] in {'-', '_'}: inc i if a[i] in {'-', '_'}: inc i
if b[j] in {'-', '_'}: inc j 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 i
inc j inc j
result = i == a.len and j == b.len result = i == a.len and j == b.len

View file

@ -73,13 +73,19 @@
## progress of the HTTP request. ## progress of the HTTP request.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## var client = newAsyncHttpClient() ## import asyncdispatch, httpclient
##
## proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} = ## proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} =
## echo("Downloaded ", progress, " of ", total) ## echo("Downloaded ", progress, " of ", total)
## echo("Current rate: ", speed div 1000, "kb/s") ## echo("Current rate: ", speed div 1000, "kb/s")
##
## proc asyncProc() {.async.} =
## var client = newAsyncHttpClient()
## client.onProgressChanged = onProgressChanged ## client.onProgressChanged = onProgressChanged
## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") ## 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``. ## If you would like to remove the callback simply set it to ``nil``.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
@ -241,7 +247,7 @@ proc parseChunks(s: Socket, timeout: int): string =
var i = 0 var i = 0
if chunkSizeStr == "": if chunkSizeStr == "":
httpError("Server terminated connection prematurely") httpError("Server terminated connection prematurely")
while true: while i < chunkSizeStr.len:
case chunkSizeStr[i] case chunkSizeStr[i]
of '0'..'9': of '0'..'9':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0')) 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) chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
of 'A'..'F': of 'A'..'F':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10) chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
of '\0':
break
of ';': of ';':
# http://tools.ietf.org/html/rfc2616#section-3.6.1 # http://tools.ietf.org/html/rfc2616#section-3.6.1
# We don't care about chunk-extensions. # We don't care about chunk-extensions.
@ -739,10 +743,11 @@ proc downloadFile*(url: string, outputFilename: string,
proc generateHeaders(requestUrl: Uri, httpMethod: string, proc generateHeaders(requestUrl: Uri, httpMethod: string,
headers: HttpHeaders, body: string, proxy: Proxy): string = headers: HttpHeaders, body: string, proxy: Proxy): string =
# GET # GET
result = httpMethod.toUpperAscii() let upperMethod = httpMethod.toUpperAscii()
result = upperMethod
result.add ' ' result.add ' '
if proxy.isNil or (not proxy.isNil and requestUrl.scheme == "https"): if proxy.isNil or requestUrl.scheme == "https":
# /path?query # /path?query
if requestUrl.path[0] != '/': result.add '/' if requestUrl.path[0] != '/': result.add '/'
result.add(requestUrl.path) result.add(requestUrl.path)
@ -768,7 +773,9 @@ proc generateHeaders(requestUrl: Uri, httpMethod: string,
add(result, "Connection: Keep-Alive\c\L") add(result, "Connection: Keep-Alive\c\L")
# Content length header. # 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") add(result, "Content-Length: " & $body.len & "\c\L")
# Proxy auth header. # Proxy auth header.
@ -929,7 +936,7 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void]
var i = 0 var i = 0
if chunkSizeStr == "": if chunkSizeStr == "":
httpError("Server terminated connection prematurely") httpError("Server terminated connection prematurely")
while true: while i < chunkSizeStr.len:
case chunkSizeStr[i] case chunkSizeStr[i]
of '0'..'9': of '0'..'9':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0')) 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) chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
of 'A'..'F': of 'A'..'F':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10) chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
of '\0':
break
of ';': of ';':
# http://tools.ietf.org/html/rfc2616#section-3.6.1 # http://tools.ietf.org/html/rfc2616#section-3.6.1
# We don't care about chunk-extensions. # We don't care about chunk-extensions.

View file

@ -190,11 +190,11 @@ proc len*(headers: HttpHeaders): int = return headers.table.len
proc parseList(line: string, list: var seq[string], start: int): int = proc parseList(line: string, list: var seq[string], start: int): int =
var i = 0 var i = 0
var current = "" 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.skipWhitespace(start + i)
i += line.parseUntil(current, {'\c', '\l', ','}, start + i) i += line.parseUntil(current, {'\c', '\l', ','}, start + i)
list.add(current) list.add(current)
if line[start + i] == ',': if start+i < line.len and line[start + i] == ',':
i.inc # Skip , i.inc # Skip ,
current.setLen(0) current.setLen(0)

View file

@ -126,7 +126,7 @@ when false:
var dataAvail = false var dataAvail = false
while dataAvail: while dataAvail:
dataAvail = recvLine(client, buf) # TODO: This is incorrect. dataAvail = recvLine(client, buf) # TODO: This is incorrect.
var L = toLower(buf.string) var L = toLowerAscii(buf.string)
if L.startsWith("content-length:"): if L.startsWith("content-length:"):
var i = len("content-length:") var i = len("content-length:")
while L[i] in Whitespace: inc(i) while L[i] in Whitespace: inc(i)
@ -199,7 +199,7 @@ when false:
notFound(client) notFound(client)
else: else:
when defined(Windows): when defined(Windows):
var ext = splitFile(path).ext.toLower var ext = splitFile(path).ext.toLowerAscii
if ext == ".exe" or ext == ".cgi": if ext == ".exe" or ext == ".cgi":
# XXX: extract interpreter information here? # XXX: extract interpreter information here?
cgi = true cgi = true
@ -303,7 +303,7 @@ proc next*(s: var Server) =
if s.reqMethod == "POST": if s.reqMethod == "POST":
# Check for Expect header # Check for Expect header
if s.headers.hasKey("Expect"): if s.headers.hasKey("Expect"):
if s.headers["Expect"].toLower == "100-continue": if s.headers["Expect"].toLowerAscii == "100-continue":
s.client.sendStatus("100 Continue") s.client.sendStatus("100 Continue")
else: else:
s.client.sendStatus("417 Expectation Failed") s.client.sendStatus("417 Expectation Failed")
@ -427,7 +427,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
if s.reqMethod == "POST": if s.reqMethod == "POST":
# Check for Expect header # Check for Expect header
if s.headers.hasKey("Expect"): if s.headers.hasKey("Expect"):
if s.headers["Expect"].toLower == "100-continue": if s.headers["Expect"].toLowerAscii == "100-continue":
s.client.sendStatus("100 Continue") s.client.sendStatus("100 Continue")
else: else:
s.client.sendStatus("417 Expectation Failed") s.client.sendStatus("417 Expectation Failed")

View file

@ -696,7 +696,7 @@ proc getBiggestInt*(n: JsonNode, default: BiggestInt = 0): BiggestInt =
else: return n.num else: return n.num
proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt {.deprecated.} = 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) getBiggestInt(n, default)
proc getFloat*(n: JsonNode, default: float = 0.0): float = 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 else: return default
proc getFNum*(n: JsonNode, default: float = 0.0): float {.deprecated.} = 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) getFloat(n, default)
proc getBool*(n: JsonNode, default: bool = false): bool = proc getBool*(n: JsonNode, default: bool = false): bool =
@ -721,7 +721,7 @@ proc getBool*(n: JsonNode, default: bool = false): bool =
else: return n.bval else: return n.bval
proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated.} = proc getBVal*(n: JsonNode, default: bool = false): bool {.deprecated.} =
## Deprecated - use getBVal instead ## **Deprecated since v0.18.2:** use ``getBool`` instead.
getBool(n, default) getBool(n, default)
proc getFields*(n: JsonNode, proc getFields*(n: JsonNode,
@ -947,7 +947,7 @@ proc contains*(node: JsonNode, val: JsonNode): bool =
find(node.elems, val) >= 0 find(node.elems, val) >= 0
proc existsKey*(node: JsonNode, key: string): bool {.deprecated.} = node.hasKey(key) 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.} = proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} =
## Sets a field from a `JObject`. ## Sets a field from a `JObject`.

View file

@ -126,7 +126,7 @@ proc substituteLog*(frmt: string, level: Level, args: varargs[string, `$`]): str
var v = "" var v = ""
let app = when defined(js): "" else: getAppFilename() let app = when defined(js): "" else: getAppFilename()
while frmt[i] in IdentChars: while frmt[i] in IdentChars:
v.add(toLower(frmt[i])) v.add(toLowerAscii(frmt[i]))
inc(i) inc(i)
case v case v
of "date": result.add(getDateStr()) of "date": result.add(getDateStr())

View file

@ -29,21 +29,21 @@ proc validEmailAddress*(s: string): bool {.noSideEffect,
chars = Letters + Digits + {'!','#','$','%','&', chars = Letters + Digits + {'!','#','$','%','&',
'\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'} '\'','*','+','/','=','?','^','_','`','{','}','|','~','-','.'}
var i = 0 var i = 0
if s[i] notin chars or s[i] == '.': return false if i >= s.len or s[i] notin chars or s[i] == '.': return false
while s[i] in chars: while i < s.len and s[i] in chars:
if s[i] == '.' and s[i+1] == '.': return false if i+1 < s.len and s[i] == '.' and s[i+1] == '.': return false
inc(i) inc(i)
if s[i] != '@': return false if i >= s.len or s[i] != '@': return false
var j = len(s)-1 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) while j >= i and s[j] in Letters: dec(j)
inc(i) # skip '@' inc(i) # skip '@'
while s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i) while i < s.len and s[i] in {'0'..'9', 'a'..'z', '-', '.'}: inc(i)
if s[i] != '\0': return false if i != s.len: return false
var x = substr(s, j+1) var x = substr(s, j+1)
if len(x) == 2 and x[0] in Letters and x[1] in Letters: return true 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", of "com", "org", "net", "gov", "mil", "biz", "info", "mobi", "name",
"aero", "jobs", "museum": return true "aero", "jobs", "museum": return true
else: return false else: return false

View file

@ -1421,8 +1421,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer,
## ##
## **Note:** This proc is not available for SSL sockets. ## **Note:** This proc is not available for SSL sockets.
assert(not socket.isClosed, "Cannot `sendTo` on a closed socket") 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: # try all possibilities:
var success = false var success = false
var it = aiList 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 function will try each IP of that hostname.
## ##
## This is the high-level version of the above ``sendTo`` function. ## 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 = proc isSsl*(socket: Socket): bool =

View file

@ -1060,18 +1060,17 @@ proc parseCmdLine*(c: string): seq[string] {.
while true: while true:
setLen(a, 0) setLen(a, 0)
# eat all delimiting whitespace # 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): when defined(windows):
# parse a single argument according to the above rules: # parse a single argument according to the above rules:
if c[i] == '\0': break
var inQuote = false var inQuote = false
while true: while i < c.len:
case c[i] case c[i]
of '\0': break
of '\\': of '\\':
var j = i var j = i
while c[j] == '\\': inc(j) while j < c.len and c[j] == '\\': inc(j)
if c[j] == '"': if j < c.len and c[j] == '"':
for k in 1..(j-i) div 2: a.add('\\') for k in 1..(j-i) div 2: a.add('\\')
if (j-i) mod 2 == 0: if (j-i) mod 2 == 0:
i = j i = j
@ -1084,7 +1083,7 @@ proc parseCmdLine*(c: string): seq[string] {.
of '"': of '"':
inc(i) inc(i)
if not inQuote: inQuote = true if not inQuote: inQuote = true
elif c[i] == '"': elif i < c.len and c[i] == '"':
a.add(c[i]) a.add(c[i])
inc(i) inc(i)
else: else:
@ -1102,13 +1101,12 @@ proc parseCmdLine*(c: string): seq[string] {.
of '\'', '\"': of '\'', '\"':
var delim = c[i] var delim = c[i]
inc(i) # skip ' or " inc(i) # skip ' or "
while c[i] != '\0' and c[i] != delim: while i < c.len and c[i] != delim:
add a, c[i] add a, c[i]
inc(i) inc(i)
if c[i] != '\0': inc(i) if i < c.len: inc(i)
of '\0': break
else: else:
while c[i] > ' ': while i < c.len and c[i] > ' ':
add(a, c[i]) add(a, c[i])
inc(i) inc(i)
add(result, a) add(result, a)

View file

@ -196,7 +196,7 @@ proc joinPath*(head, tail: string): string {.
else: else:
result = head & tail result = head & tail
else: else:
if tail[0] in {DirSep, AltSep}: if tail.len > 0 and tail[0] in {DirSep, AltSep}:
result = head & tail result = head & tail
else: else:
result = head & DirSep & tail result = head & DirSep & tail
@ -477,7 +477,7 @@ proc unixToNativePath*(path: string, drive=""): string {.
var i = start var i = start
while i < len(path): # ../../../ --> :::: 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 # parent directory
when defined(macos): when defined(macos):
if result[high(result)] == ':': if result[high(result)] == ':':

View file

@ -57,26 +57,26 @@ type
{.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].} {.deprecated: [TCmdLineKind: CmdLineKind, TOptParser: OptParser].}
proc parseWord(s: string, i: int, w: var string, proc parseWord(s: string, i: int, w: var string,
delim: set[char] = {'\x09', ' ', '\0'}): int = delim: set[char] = {'\x09', ' '}): int =
result = i result = i
if s[result] == '\"': if result < s.len and s[result] == '\"':
inc(result) inc(result)
while not (s[result] in {'\0', '\"'}): while result < s.len and s[result] != '\"':
add(w, s[result]) add(w, s[result])
inc(result) inc(result)
if s[result] == '\"': inc(result) if result < s.len and s[result] == '\"': inc(result)
else: else:
while not (s[result] in delim): while result < s.len and s[result] notin delim:
add(w, s[result]) add(w, s[result])
inc(result) inc(result)
when declared(os.paramCount): when declared(os.paramCount):
proc quote(s: string): string = 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] == '-': if s[0] == '-':
result = newStringOfCap(s.len) result = newStringOfCap(s.len)
var i = parseWord(s, 0, result, {'\0', ' ', '\x09', ':', '='}) var i = parseWord(s, 0, result, {' ', '\x09', ':', '='})
if s[i] in {':','='}: if i < s.len and s[i] in {':','='}:
result.add s[i] result.add s[i]
inc i inc i
result.add '"' result.add '"'
@ -144,43 +144,45 @@ proc handleShortOption(p: var OptParser) =
add(p.key.string, p.cmd[i]) add(p.key.string, p.cmd[i])
inc(i) inc(i)
p.inShortState = true p.inShortState = true
while p.cmd[i] in {'\x09', ' '}: while i < p.cmd.len and p.cmd[i] in {'\x09', ' '}:
inc(i) inc(i)
p.inShortState = false p.inShortState = false
if 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 {':', '='} or
if p.cmd[i] in {':', '='}: card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal:
if i < p.cmd.len and p.cmd[i] in {':', '='}:
inc(i) inc(i)
p.inShortState = false 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) 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 p.pos = i
proc next*(p: var OptParser) {.rtl, extern: "npo$1".} = proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
## parses the first or next option; ``p.kind`` describes what token has been ## parses the first or next option; ``p.kind`` describes what token has been
## parsed. ``p.key`` and ``p.val`` are set accordingly. ## parsed. ``p.key`` and ``p.val`` are set accordingly.
var i = p.pos 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 p.pos = i
setLen(p.key.string, 0) setLen(p.key.string, 0)
setLen(p.val.string, 0) setLen(p.val.string, 0)
if p.inShortState: if p.inShortState:
handleShortOption(p) handleShortOption(p)
return return
case p.cmd[i] if i >= p.cmd.len:
of '\0':
p.kind = cmdEnd p.kind = cmdEnd
of '-': return
inc(i)
if p.cmd[i] == '-': if p.cmd[i] == '-':
inc(i)
if i < p.cmd.len and p.cmd[i] == '-':
p.kind = cmdLongOption p.kind = cmdLongOption
inc(i) inc(i)
i = parseWord(p.cmd, i, p.key.string, {'\0', ' ', '\x09', ':', '='}) i = parseWord(p.cmd, i, p.key.string, {' ', '\x09', ':', '='})
while p.cmd[i] in {'\x09', ' '}: inc(i) while i < p.cmd.len and 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 i < p.cmd.len and p.cmd[i] in {':', '='} or
if p.cmd[i] in {':', '='}: len(p.longNoVal) > 0 and p.key.string notin p.longNoVal:
if i < p.cmd.len and p.cmd[i] in {':', '='}:
inc(i) 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) p.pos = parseWord(p.cmd, i, p.val.string)
else: else:
p.pos = i p.pos = i

View file

@ -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. ## upper bound. Not more than ```maxLen`` characters are parsed.
var i = start var i = start
var foundDigit = false 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 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: while i < last:
case s[i] case s[i]
of '_': discard 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. ## the number of the parsed characters or 0 in case of an error.
var i = start var i = start
var foundDigit = false var foundDigit = false
if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
while true: while i < s.len:
case s[i] case s[i]
of '_': discard of '_': discard
of '0'..'7': 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. ## the number of the parsed characters or 0 in case of an error.
var i = start var i = start
var foundDigit = false var foundDigit = false
if s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2) if i+1 < s.len and s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2)
while true: while i < s.len:
case s[i] case s[i]
of '_': discard of '_': discard
of '0'..'1': 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 ## parses an identifier and stores it in ``ident``. Returns
## the number of the parsed characters or 0 in case of an error. ## the number of the parsed characters or 0 in case of an error.
var i = start var i = start
if s[i] in IdentStartChars: if i < s.len and s[i] in IdentStartChars:
inc(i) 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) ident = substr(s, start, i-1)
result = i-start 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. ## Returns the parsed identifier or an empty string in case of an error.
result = "" result = ""
var i = start var i = start
if i < s.len and s[i] in IdentStartChars:
if s[i] in IdentStartChars:
inc(i) 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) result = substr(s, start, i-1)
proc parseToken*(s: string, token: var string, validChars: set[char], 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. ## **Deprecated since version 0.8.12**: Use ``parseWhile`` instead.
var i = start var i = start
while s[i] in validChars: inc(i) while i < s.len and s[i] in validChars: inc(i)
result = i-start result = i-start
token = substr(s, start, i-1) token = substr(s, start, i-1)
proc skipWhitespace*(s: string, start = 0): int {.inline.} = proc skipWhitespace*(s: string, start = 0): int {.inline.} =
## skips the whitespace starting at ``s[start]``. Returns the number of ## skips the whitespace starting at ``s[start]``. Returns the number of
## skipped characters. ## 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.} = proc skip*(s, token: string, start = 0): int {.inline.} =
## skips the `token` starting at ``s[start]``. Returns the length of `token` ## skips the `token` starting at ``s[start]``. Returns the length of `token`
## or 0 if there was no `token` at ``s[start]``. ## 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 if result != token.len: result = 0
proc skipIgnoreCase*(s, token: string, start = 0): int = proc skipIgnoreCase*(s, token: string, start = 0): int =
## same as `skip` but case is ignored for token matching. ## 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) toLower(s[result+start]) == toLower(token[result]): inc(result)
if result != token.len: result = 0 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 ## Skips all characters until one char from the set `until` is found
## or the end is reached. ## or the end is reached.
## Returns number of characters skipped. ## 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.} = proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
## Skips all characters until the char `until` is found ## Skips all characters until the char `until` is found
## or the end is reached. ## or the end is reached.
## Returns number of characters skipped. ## 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.} = proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
## Skips all characters while one char from the set `token` is found. ## Skips all characters while one char from the set `token` is found.
## Returns number of characters skipped. ## 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], proc parseUntil*(s: string, token: var string, until: set[char],
start = 0): int {.inline.} = 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 ## the number of the parsed characters or 0 in case of an error. A token
## consists of the characters in `validChars`. ## consists of the characters in `validChars`.
var i = start var i = start
while s[i] in validChars: inc(i) while i < s.len and s[i] in validChars: inc(i)
result = i-start result = i-start
token = substr(s, start, i-1) token = substr(s, start, i-1)
@ -231,16 +231,17 @@ proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
var var
sign: BiggestInt = -1 sign: BiggestInt = -1
i = start i = start
if i < s.len:
if s[i] == '+': inc(i) if s[i] == '+': inc(i)
elif s[i] == '-': elif s[i] == '-':
inc(i) inc(i)
sign = 1 sign = 1
if s[i] in {'0'..'9'}: if i < s.len and s[i] in {'0'..'9'}:
b = 0 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')) b = b * 10 - (ord(s[i]) - ord('0'))
inc(i) 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 b = b * sign
result = i - start result = i - start
{.pop.} # overflowChecks {.pop.} # overflowChecks
@ -281,17 +282,17 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int =
## discard parseSaturatedNatural("848", res) ## discard parseSaturatedNatural("848", res)
## doAssert res == 848 ## doAssert res == 848
var i = start var i = start
if s[i] == '+': inc(i) if i < s.len and s[i] == '+': inc(i)
if s[i] in {'0'..'9'}: if i < s.len and s[i] in {'0'..'9'}:
b = 0 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') let c = ord(s[i]) - ord('0')
if b <= (high(int) - c) div 10: if b <= (high(int) - c) div 10:
b = b * 10 + c b = b * 10 + c
else: else:
b = high(int) b = high(int)
inc(i) 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 result = i - start
# overflowChecks doesn't work with BiggestUInt # overflowChecks doesn't work with BiggestUInt
@ -300,16 +301,16 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
res = 0.BiggestUInt res = 0.BiggestUInt
prev = 0.BiggestUInt prev = 0.BiggestUInt
i = start i = start
if s[i] == '+': inc(i) # Allow if i < s.len and s[i] == '+': inc(i) # Allow
if s[i] in {'0'..'9'}: if i < s.len and s[i] in {'0'..'9'}:
b = 0 b = 0
while s[i] in {'0'..'9'}: while i < s.len and s[i] in {'0'..'9'}:
prev = res prev = res
res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
if prev > res: if prev > res:
return 0 # overflowChecks emulation return 0 # overflowChecks emulation
inc(i) 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 b = res
result = i - start result = i - start
@ -389,31 +390,31 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
var kind: InterpolatedKind var kind: InterpolatedKind
while true: while true:
var j = i var j = i
if s[j] == '$': if j < s.len and s[j] == '$':
if s[j+1] == '{': if j+1 < s.len and s[j+1] == '{':
inc j, 2 inc j, 2
var nesting = 0 var nesting = 0
while true: block curlies:
while j < s.len:
case s[j] case s[j]
of '{': inc nesting of '{': inc nesting
of '}': of '}':
if nesting == 0: if nesting == 0:
inc j inc j
break break curlies
dec nesting dec nesting
of '\0':
raise newException(ValueError,
"Expected closing '}': " & substr(s, i, s.high))
else: discard else: discard
inc j inc j
raise newException(ValueError,
"Expected closing '}': " & substr(s, i, s.high))
inc i, 2 # skip ${ inc i, 2 # skip ${
kind = ikExpr kind = ikExpr
elif s[j+1] in IdentStartChars: elif j+1 < s.len and s[j+1] in IdentStartChars:
inc j, 2 inc j, 2
while s[j] in IdentChars: inc(j) while j < s.len and s[j] in IdentChars: inc(j)
inc i # skip $ inc i # skip $
kind = ikVar kind = ikVar
elif s[j+1] == '$': elif j+1 < s.len and s[j+1] == '$':
inc j, 2 inc j, 2
inc i # skip $ inc i # skip $
kind = ikDollar kind = ikDollar

View file

@ -534,15 +534,15 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
case p.kind case p.kind
of pkEmpty: result = 0 # match of length 0 of pkEmpty: result = 0 # match of length 0
of pkAny: of pkAny:
if s[start] != '\0': result = 1 if start < s.len: result = 1
else: result = -1 else: result = -1
of pkAnyRune: of pkAnyRune:
if s[start] != '\0': if start < s.len:
result = runeLenAt(s, start) result = runeLenAt(s, start)
else: else:
result = -1 result = -1
of pkLetter: of pkLetter:
if s[start] != '\0': if start < s.len:
var a: Rune var a: Rune
result = start result = start
fastRuneAt(s, result, a) fastRuneAt(s, result, a)
@ -551,7 +551,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
else: else:
result = -1 result = -1
of pkLower: of pkLower:
if s[start] != '\0': if start < s.len:
var a: Rune var a: Rune
result = start result = start
fastRuneAt(s, result, a) fastRuneAt(s, result, a)
@ -560,7 +560,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
else: else:
result = -1 result = -1
of pkUpper: of pkUpper:
if s[start] != '\0': if start < s.len:
var a: Rune var a: Rune
result = start result = start
fastRuneAt(s, result, a) fastRuneAt(s, result, a)
@ -569,7 +569,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
else: else:
result = -1 result = -1
of pkTitle: of pkTitle:
if s[start] != '\0': if start < s.len:
var a: Rune var a: Rune
result = start result = start
fastRuneAt(s, result, a) fastRuneAt(s, result, a)
@ -578,7 +578,7 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
else: else:
result = -1 result = -1
of pkWhitespace: of pkWhitespace:
if s[start] != '\0': if start < s.len:
var a: Rune var a: Rune
result = start result = start
fastRuneAt(s, result, a) fastRuneAt(s, result, a)
@ -589,15 +589,15 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
of pkGreedyAny: of pkGreedyAny:
result = len(s) - start result = len(s) - start
of pkNewLine: of pkNewLine:
if s[start] == '\L': result = 1 if start < s.len and s[start] == '\L': result = 1
elif s[start] == '\C': elif start < s.len and s[start] == '\C':
if s[start+1] == '\L': result = 2 if start+1 < s.len and s[start+1] == '\L': result = 2
else: result = 1 else: result = 1
else: result = -1 else: result = -1
of pkTerminal: of pkTerminal:
result = len(p.term) result = len(p.term)
for i in 0..result-1: 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 result = -1
break break
of pkTerminalIgnoreCase: of pkTerminalIgnoreCase:
@ -606,6 +606,9 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
a, b: Rune a, b: Rune
result = start result = start
while i < len(p.term): while i < len(p.term):
if result >= s.len:
result = -1
break
fastRuneAt(p.term, i, a) fastRuneAt(p.term, i, a)
fastRuneAt(s, result, b) fastRuneAt(s, result, b)
if toLower(a) != toLower(b): if toLower(a) != toLower(b):
@ -621,18 +624,23 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
while true: while true:
fastRuneAt(p.term, i, a) fastRuneAt(p.term, i, a)
if a != Rune('_'): break if a != Rune('_'): break
while true: while result < s.len:
fastRuneAt(s, result, b) fastRuneAt(s, result, b)
if b != Rune('_'): break 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 result = -1
break break
dec(result, start) dec(result, start)
of pkChar: of pkChar:
if p.ch == s[start]: result = 1 if start < s.len and p.ch == s[start]: result = 1
else: result = -1 else: result = -1
of pkCharChoice: 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 else: result = -1
of pkNonTerminal: of pkNonTerminal:
var oldMl = c.ml var oldMl = c.ml
@ -695,10 +703,10 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int {.
of pkGreedyRepChar: of pkGreedyRepChar:
result = 0 result = 0
var ch = p.ch 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: of pkGreedyRepSet:
result = 0 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: of pkOption:
result = max(0, rawMatch(s, p.sons[0], start, c)) result = max(0, rawMatch(s, p.sons[0], start, c))
of pkAndPredicate: of pkAndPredicate:

View file

@ -278,7 +278,7 @@ template callFormatOption(res, arg, option) {.dirty.} =
macro `&`*(pattern: string): untyped = macro `&`*(pattern: string): untyped =
## For a specification of the ``&`` macro, see the module level documentation. ## For a specification of the ``&`` macro, see the module level documentation.
if pattern.kind notin {nnkStrLit..nnkTripleStrLit}: 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 let f = pattern.strVal
var i = 0 var i = 0
let res = genSym(nskVar, "fmtRes") let res = genSym(nskVar, "fmtRes")

View file

@ -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 = proc someSep(input: string; start: int; seps: set[char] = {':','-','.'}): int =
# Note: The parameters and return value must match to what ``scanf`` requires # Note: The parameters and return value must match to what ``scanf`` requires
result = 0 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): if scanf(input, "$w$[someSep]$w", key, value):
... ...
@ -231,7 +231,7 @@ is performed.
var i = start var i = start
var u = 0 var u = 0
while true: while true:
if s[i] == '\0' or s[i] == unless: if i >= s.len or s[i] == unless:
return 0 return 0
elif s[i] == until[0]: elif s[i] == until[0]:
u = 1 u = 1
@ -315,6 +315,8 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
conds.add resLen.notZero conds.add resLen.notZero
conds.add resLen conds.add resLen
template at(s: string; i: int): char = (if i < s.len: s[i] else: '\0')
var i = 0 var i = 0
var p = 0 var p = 0
var idx = genSym(nskVar, "idx") var idx = genSym(nskVar, "idx")
@ -397,7 +399,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
var nesting = 0 var nesting = 0
let start = p let start = p
while true: while true:
case pattern[p] case pattern.at(p)
of '{': inc nesting of '{': inc nesting
of '}': of '}':
if nesting == 0: break if nesting == 0: break
@ -419,7 +421,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
var nesting = 0 var nesting = 0
let start = p let start = p
while true: while true:
case pattern[p] case pattern.at(p)
of '[': inc nesting of '[': inc nesting
of ']': of ']':
if nesting == 0: break 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 = template atom*(input: string; idx: int; c: char): bool =
## Used in scanp for the matching of atoms (usually chars). ## 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 = 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 prepare*(input: string): int = 0
template success*(x: int): bool = x != 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) template nxt*(input: string; idx, step: int = 1) = inc(idx, step)
macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = 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] type StmtTriple = tuple[init, cond, action: NimNode]
template interf(x): untyped = bindSym(x, brForceOpen) 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)) !!newCall(interf"nxt", input, idx, resLen))
of nnkCallKinds: of nnkCallKinds:
# *{'A'..'Z'} !! s.add(!_) # *{'A'..'Z'} !! s.add(!_)
template buildWhile(init, cond, action): untyped = template buildWhile(input, idx, init, cond, action): untyped =
while true: while hasNxt(input, idx):
init init
if not cond: break if not cond: break
action action
@ -528,7 +532,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
!!newCall(interf"nxt", input, idx, it[2])) !!newCall(interf"nxt", input, idx, it[2]))
elif it.kind == nnkPrefix and it[0].eqIdent"*": elif it.kind == nnkPrefix and it[0].eqIdent"*":
let (init, cond, action) = atm(it[1], input, idx, attached) 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()) newEmptyNode(), newEmptyNode())
elif it.kind == nnkPrefix and it[0].eqIdent"+": elif it.kind == nnkPrefix and it[0].eqIdent"+":
# x+ is the same as xx* # x+ is the same as xx*
@ -621,7 +625,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool =
when isMainModule: when isMainModule:
proc twoDigits(input: string; x: var int; start: int): int = 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 result = 2
x = 13 x = 13
else: else:
@ -629,10 +633,10 @@ when isMainModule:
proc someSep(input: string; start: int; seps: set[char] = {';',',','-','.'}): int = proc someSep(input: string; start: int; seps: set[char] = {';',',','-','.'}): int =
result = 0 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 = 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 = "" res = ""
while result+start < s.len and s[result+start] > ' ' and s[result+start] != '_': while result+start < s.len and s[result+start] > ' ' and s[result+start] != '_':
res.add s[result+start] res.add s[result+start]
@ -652,7 +656,7 @@ when isMainModule:
var info = "" var info = ""
if scanp(resp, idx, *`whites`, '#', *`digits`, +`whites`, ?("0x", *`hexdigits`, " in "), if scanp(resp, idx, *`whites`, '#', *`digits`, +`whites`, ?("0x", *`hexdigits`, " in "),
demangle($input, prc, $index), *`whites`, '(', * ~ ')', ')', demangle($input, prc, $index), *`whites`, '(', * ~ ')', ')',
*`whites`, "at ", +(~{'\C', '\L', '\0'} -> info.add($_)) ): *`whites`, "at ", +(~{'\C', '\L'} -> info.add($_)) ):
result.add prc & " " & info result.add prc & " " & info
else: else:
break break
@ -713,7 +717,7 @@ when isMainModule:
"NimMainInner c:/users/anwender/projects/nim/lib/system.nim:2605", "NimMainInner c:/users/anwender/projects/nim/lib/system.nim:2605",
"NimMain c:/users/anwender/projects/nim/lib/system.nim:2613", "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613",
"main c:/users/anwender/projects/nim/lib/system.nim:2620"] "main c:/users/anwender/projects/nim/lib/system.nim:2620"]
doAssert parseGDB(gdbOut) == result #doAssert parseGDB(gdbOut) == result
# bug #6487 # bug #6487
var count = 0 var count = 0

View file

@ -29,7 +29,7 @@ type
modeCaseSensitive, ## the table is case sensitive modeCaseSensitive, ## the table is case sensitive
modeCaseInsensitive, ## the table is case insensitive modeCaseInsensitive, ## the table is case insensitive
modeStyleInsensitive ## the table is style insensitive modeStyleInsensitive ## the table is style insensitive
KeyValuePair = tuple[key, val: string] KeyValuePair = tuple[key, val: string, hasValue: bool]
KeyValuePairSeq = seq[KeyValuePair] KeyValuePairSeq = seq[KeyValuePair]
StringTableObj* = object of RootObj StringTableObj* = object of RootObj
counter: int counter: int
@ -48,19 +48,19 @@ proc len*(t: StringTableRef): int {.rtlFunc, extern: "nst$1".} =
iterator pairs*(t: StringTableRef): tuple[key, value: string] = iterator pairs*(t: StringTableRef): tuple[key, value: string] =
## iterates over every (key, value) pair in the table `t`. ## iterates over every (key, value) pair in the table `t`.
for h in 0..high(t.data): 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) yield (t.data[h].key, t.data[h].val)
iterator keys*(t: StringTableRef): string = iterator keys*(t: StringTableRef): string =
## iterates over every key in the table `t`. ## iterates over every key in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if not isNil(t.data[h].key): if t.data[h].hasValue:
yield t.data[h].key yield t.data[h].key
iterator values*(t: StringTableRef): string = iterator values*(t: StringTableRef): string =
## iterates over every value in the table `t`. ## iterates over every value in the table `t`.
for h in 0..high(t.data): for h in 0..high(t.data):
if not isNil(t.data[h].key): if t.data[h].hasValue:
yield t.data[h].val yield t.data[h].val
type type
@ -102,7 +102,7 @@ proc nextTry(h, maxHash: Hash): Hash {.inline.} =
proc rawGet(t: StringTableRef, key: string): int = proc rawGet(t: StringTableRef, key: string): int =
var h: Hash = myhash(t, key) and high(t.data) # start with real hash value 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): if myCmp(t, t.data[h].key, key):
return h return h
h = nextTry(h, high(t.data)) 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) = proc rawInsert(t: StringTableRef, data: var KeyValuePairSeq, key, val: string) =
var h: Hash = myhash(t, key) and high(data) var h: Hash = myhash(t, key) and high(data)
while not isNil(data[h].key): while data[h].hasValue:
h = nextTry(h, high(data)) h = nextTry(h, high(data))
data[h].key = key data[h].key = key
data[h].val = val data[h].val = val
data[h].hasValue = true
proc enlarge(t: StringTableRef) = proc enlarge(t: StringTableRef) =
var n: KeyValuePairSeq var n: KeyValuePairSeq
newSeq(n, len(t.data) * growthFactor) newSeq(n, len(t.data) * growthFactor)
for i in countup(0, high(t.data)): 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) swap(t.data, n)
proc `[]=`*(t: StringTableRef, key, val: string) {.rtlFunc, extern: "nstPut".} = proc `[]=`*(t: StringTableRef, key, val: string) {.rtlFunc, extern: "nstPut".} =
@ -198,8 +199,7 @@ proc clear*(s: StringTableRef, mode: StringTableMode) =
s.counter = 0 s.counter = 0
s.data.setLen(startSize) s.data.setLen(startSize)
for i in 0..<s.data.len: for i in 0..<s.data.len:
if not isNil(s.data[i].key): s.data[i].hasValue = false
s.data[i].key = nil
proc newStringTable*(keyValuePairs: varargs[string], proc newStringTable*(keyValuePairs: varargs[string],
mode: StringTableMode): StringTableRef {. mode: StringTableMode): StringTableRef {.

View file

@ -106,6 +106,12 @@ proc isUpperAscii*(c: char): bool {.noSideEffect, procvar,
## This checks ASCII characters only. ## This checks ASCII characters only.
return c in {'A'..'Z'} return c in {'A'..'Z'}
template isImpl(call) =
if s.len == 0: return false
result = true
for c in s:
if not call(c): return false
proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar, proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsAlphaAsciiStr".} = rtl, extern: "nsuIsAlphaAsciiStr".} =
## Checks whether or not `s` is alphabetical. ## Checks whether or not `s` is alphabetical.
@ -114,12 +120,7 @@ proc isAlphaAscii*(s: string): bool {.noSideEffect, procvar,
## Returns true if all characters in `s` are ## Returns true if all characters in `s` are
## alphabetic and there is at least one character ## alphabetic and there is at least one character
## in `s`. ## in `s`.
if s.len() == 0: isImpl isAlphaAscii
return false
result = true
for c in s:
if not c.isAlphaAscii(): return false
proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar, proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsAlphaNumericStr".} = rtl, extern: "nsuIsAlphaNumericStr".} =
@ -129,13 +130,7 @@ proc isAlphaNumeric*(s: string): bool {.noSideEffect, procvar,
## Returns true if all characters in `s` are ## Returns true if all characters in `s` are
## alpanumeric and there is at least one character ## alpanumeric and there is at least one character
## in `s`. ## in `s`.
if s.len() == 0: isImpl isAlphaNumeric
return false
result = true
for c in s:
if not c.isAlphaNumeric():
return false
proc isDigit*(s: string): bool {.noSideEffect, procvar, proc isDigit*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsDigitStr".} = rtl, extern: "nsuIsDigitStr".} =
@ -145,13 +140,7 @@ proc isDigit*(s: string): bool {.noSideEffect, procvar,
## Returns true if all characters in `s` are ## Returns true if all characters in `s` are
## numeric and there is at least one character ## numeric and there is at least one character
## in `s`. ## in `s`.
if s.len() == 0: isImpl isDigit
return false
result = true
for c in s:
if not c.isDigit():
return false
proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar, proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsSpaceAsciiStr".} = rtl, extern: "nsuIsSpaceAsciiStr".} =
@ -159,13 +148,7 @@ proc isSpaceAscii*(s: string): bool {.noSideEffect, procvar,
## ##
## Returns true if all characters in `s` are whitespace ## Returns true if all characters in `s` are whitespace
## characters and there is at least one character in `s`. ## characters and there is at least one character in `s`.
if s.len() == 0: isImpl isSpaceAscii
return false
result = true
for c in s:
if not c.isSpaceAscii():
return false
proc isLowerAscii*(s: string): bool {.noSideEffect, procvar, proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsLowerAsciiStr".} = rtl, extern: "nsuIsLowerAsciiStr".} =
@ -174,13 +157,7 @@ proc isLowerAscii*(s: string): bool {.noSideEffect, procvar,
## This checks ASCII characters only. ## This checks ASCII characters only.
## Returns true if all characters in `s` are lower case ## Returns true if all characters in `s` are lower case
## and there is at least one character in `s`. ## and there is at least one character in `s`.
if s.len() == 0: isImpl isLowerAscii
return false
for c in s:
if not c.isLowerAscii():
return false
true
proc isUpperAscii*(s: string): bool {.noSideEffect, procvar, proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
rtl, extern: "nsuIsUpperAsciiStr".} = rtl, extern: "nsuIsUpperAsciiStr".} =
@ -189,13 +166,7 @@ proc isUpperAscii*(s: string): bool {.noSideEffect, procvar,
## This checks ASCII characters only. ## This checks ASCII characters only.
## Returns true if all characters in `s` are upper case ## Returns true if all characters in `s` are upper case
## and there is at least one character in `s`. ## and there is at least one character in `s`.
if s.len() == 0: isImpl isUpperAscii
return false
for c in s:
if not c.isUpperAscii():
return false
true
proc toLowerAscii*(c: char): char {.noSideEffect, procvar, proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
rtl, extern: "nsuToLowerAsciiChar".} = rtl, extern: "nsuToLowerAsciiChar".} =
@ -209,6 +180,11 @@ proc toLowerAscii*(c: char): char {.noSideEffect, procvar,
else: else:
result = c result = c
template toImpl(call) =
result = newString(len(s))
for i in 0..len(s) - 1:
result[i] = call(s[i])
proc toLowerAscii*(s: string): string {.noSideEffect, procvar, proc toLowerAscii*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuToLowerAsciiStr".} = rtl, extern: "nsuToLowerAsciiStr".} =
## Converts `s` into lower case. ## Converts `s` into lower case.
@ -216,9 +192,7 @@ proc toLowerAscii*(s: string): string {.noSideEffect, procvar,
## This works only for the letters ``A-Z``. See `unicode.toLower ## This works only for the letters ``A-Z``. See `unicode.toLower
## <unicode.html#toLower>`_ for a version that works for any Unicode ## <unicode.html#toLower>`_ for a version that works for any Unicode
## character. ## character.
result = newString(len(s)) toImpl toLowerAscii
for i in 0..len(s) - 1:
result[i] = toLowerAscii(s[i])
proc toUpperAscii*(c: char): char {.noSideEffect, procvar, proc toUpperAscii*(c: char): char {.noSideEffect, procvar,
rtl, extern: "nsuToUpperAsciiChar".} = 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 ## This works only for the letters ``A-Z``. See `unicode.toUpper
## <unicode.html#toUpper>`_ for a version that works for any Unicode ## <unicode.html#toUpper>`_ for a version that works for any Unicode
## character. ## character.
result = newString(len(s)) toImpl toUpperAscii
for i in 0..len(s) - 1:
result[i] = toUpperAscii(s[i])
proc capitalizeAscii*(s: string): string {.noSideEffect, procvar, proc capitalizeAscii*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuCapitalizeAscii".} = rtl, extern: "nsuCapitalizeAscii".} =
## Converts the first character of `s` into upper case. ## Converts the first character of `s` into upper case.
## ##
## This works only for the letters ``A-Z``. ## This works only for the letters ``A-Z``.
result = toUpperAscii(s[0]) & substr(s, 1) if s.len == 0: result = ""
else: 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
## <unicode.html#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
## <unicode.html#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
## <unicode.html#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
## <unicode.html#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)
proc normalize*(s: string): string {.noSideEffect, procvar, proc normalize*(s: string): string {.noSideEffect, procvar,
rtl, extern: "nsuNormalize".} = rtl, extern: "nsuNormalize".} =
@ -429,14 +271,22 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
var i = 0 var i = 0
var j = 0 var j = 0
while true: while true:
while a[i] == '_': inc(i) while i < a.len and a[i] == '_': inc i
while b[j] == '_': inc(j) # BUGFIX: typo while j < b.len and b[j] == '_': inc j
var aa = toLowerAscii(a[i]) var aa = if i < a.len: toLowerAscii(a[i]) else: '\0'
var bb = toLowerAscii(b[j]) var bb = if j < b.len: toLowerAscii(b[j]) else: '\0'
result = ord(aa) - ord(bb) result = ord(aa) - ord(bb)
if result != 0 or aa == '\0': break if result != 0: return result
inc(i) # the characters are identical:
inc(j) 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, proc strip*(s: string, leading = true, trailing = true,
chars: set[char] = Whitespace): string chars: set[char] = Whitespace): string
@ -451,7 +301,7 @@ proc strip*(s: string, leading = true, trailing = true,
first = 0 first = 0
last = len(s)-1 last = len(s)-1
if leading: if leading:
while s[first] in chars: inc(first) while first <= last and s[first] in chars: inc(first)
if trailing: if trailing:
while last >= 0 and s[last] in chars: dec(last) while last >= 0 and s[last] in chars: dec(last)
result = substr(s, first, 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')) result[i] = chr(val mod 8 + ord('0'))
val = val div 8 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. ## Checks if `s` is nil or empty.
result = len(s) == 0 result = len(s) == 0
@ -486,7 +338,6 @@ proc substrEq(s: string, pos: int, substr: string): bool =
var length = substr.len var length = substr.len
while i < length and s[pos+i] == substr[i]: while i < length and s[pos+i] == substr[i]:
inc i inc i
return i == length return i == length
# --------- Private templates for different split separators ----------- # --------- Private templates for different split separators -----------
@ -520,7 +371,7 @@ template oldSplit(s, seps, maxsplit) =
var splits = maxsplit var splits = maxsplit
assert(not ('\0' in seps)) assert(not ('\0' in seps))
while last < len(s): 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 var first = last
while last < len(s) and s[last] notin seps: inc(last) while last < len(s) and s[last] notin seps: inc(last)
if first <= last-1: if first <= last-1:
@ -571,9 +422,6 @@ iterator split*(s: string, seps: set[char] = Whitespace,
## "08" ## "08"
## "08.398990" ## "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 = iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
@ -660,7 +508,6 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string =
## "is" ## "is"
## "corrupted" ## "corrupted"
## ##
splitCommon(s, sep, maxsplit, sep.len) splitCommon(s, sep, maxsplit, sep.len)
template rsplitCommon(s, sep, maxsplit, sepLen) = template rsplitCommon(s, sep, maxsplit, sepLen) =
@ -670,29 +517,21 @@ template rsplitCommon(s, sep, maxsplit, sepLen) =
first = last first = last
splits = maxsplit splits = maxsplit
startPos = 0 startPos = 0
# go to -1 in order to get separators at the beginning # go to -1 in order to get separators at the beginning
while first >= -1: while first >= -1:
while first >= 0 and not stringHasSep(s, first, sep): while first >= 0 and not stringHasSep(s, first, sep):
dec(first) dec(first)
if splits == 0: if splits == 0:
# No more splits means set first to the beginning # No more splits means set first to the beginning
first = -1 first = -1
if first == -1: if first == -1:
startPos = 0 startPos = 0
else: else:
startPos = first + sepLen startPos = first + sepLen
yield substr(s, startPos, last) yield substr(s, startPos, last)
if splits == 0: break
if splits == 0:
break
dec(splits) dec(splits)
dec(first) dec(first)
last = first last = first
iterator rsplit*(s: string, seps: set[char] = Whitespace, iterator rsplit*(s: string, seps: set[char] = Whitespace,
@ -712,7 +551,6 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace,
## "foo" ## "foo"
## ##
## Substrings are separated from the right by the set of chars `seps` ## Substrings are separated from the right by the set of chars `seps`
rsplitCommon(s, seps, maxsplit, 1) rsplitCommon(s, seps, maxsplit, 1)
iterator rsplit*(s: string, sep: char, iterator rsplit*(s: string, sep: char,
@ -779,14 +617,14 @@ iterator splitLines*(s: string): string =
var first = 0 var first = 0
var last = 0 var last = 0
while true: 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) yield substr(s, first, last-1)
# skip newlines: # skip newlines:
if last >= s.len: break
if s[last] == '\l': inc(last) if s[last] == '\l': inc(last)
elif s[last] == '\c': elif s[last] == '\c':
inc(last) inc(last)
if s[last] == '\l': inc(last) if last < s.len and s[last] == '\l': inc(last)
else: break # was '\0'
first = last first = last
proc splitLines*(s: string): seq[string] {.noSideEffect, proc splitLines*(s: string): seq[string] {.noSideEffect,
@ -811,7 +649,7 @@ proc countLines*(s: string): int {.noSideEffect,
while i < s.len: while i < s.len:
case s[i] case s[i]
of '\c': of '\c':
if s[i+1] == '\l': inc i if i+1 < s.len and s[i+1] == '\l': inc i
inc result inc result
of '\l': inc result of '\l': inc result
else: discard else: discard
@ -1025,9 +863,9 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores ## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores
## within `s` are ignored. ## within `s` are ignored.
var i = 0 var i = 0
if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) if i+1 < s.len and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
elif s[i] == '#': inc(i) elif i < s.len and s[i] == '#': inc(i)
while true: while i < s.len:
case s[i] case s[i]
of '_': inc(i) of '_': inc(i)
of '0'..'9': of '0'..'9':
@ -1039,7 +877,6 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
of 'A'..'F': of 'A'..'F':
result = result shl 4 or (ord(s[i]) - ord('A') + 10) result = result shl 4 or (ord(s[i]) - ord('A') + 10)
inc(i) inc(i)
of '\0': break
else: raise newException(ValueError, "invalid integer: " & s) else: raise newException(ValueError, "invalid integer: " & s)
proc generateHexCharToValueMap(): string = proc generateHexCharToValueMap(): string =
@ -1148,14 +985,6 @@ template spaces*(n: Natural): string = repeat(' ', n)
## echo text1 & spaces(max(0, width - text1.len)) & "|" ## echo text1 & spaces(max(0, width - text1.len)) & "|"
## echo text2 & spaces(max(0, width - text2.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 {. proc align*(s: string, count: Natural, padding = ' '): string {.
noSideEffect, rtl, extern: "nsuAlignString".} = noSideEffect, rtl, extern: "nsuAlignString".} =
## Aligns a string `s` with `padding`, so that it is of length `count`. ## 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 var i = 0
while true: while true:
var j = i 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) while j < s.len and (s[j] in seps) == isSep: inc(j)
if j > i: if j > i:
yield (substr(s, i, j-1), isSep) yield (substr(s, i, j-1), isSep)
@ -1297,7 +1126,7 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string
var indentCount = 0 var indentCount = 0
for j in 0..<count.int: for j in 0..<count.int:
indentCount.inc indentCount.inc
if line[j .. j + padding.len-1] != padding: if j + padding.len-1 >= line.len or line[j .. j + padding.len-1] != padding:
indentCount = j indentCount = j
break break
result.add(line[indentCount*padding.len .. ^1]) result.add(line[indentCount*padding.len .. ^1])
@ -1325,13 +1154,13 @@ proc startsWith*(s, prefix: string): bool {.noSideEffect,
## If ``prefix == ""`` true is returned. ## If ``prefix == ""`` true is returned.
var i = 0 var i = 0
while true: while true:
if prefix[i] == '\0': return true if i >= prefix.len: return true
if s[i] != prefix[i]: return false if i >= s.len or s[i] != prefix[i]: return false
inc(i) inc(i)
proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} = proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} =
## Returns true iff ``s`` starts with ``prefix``. ## 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, proc endsWith*(s, suffix: string): bool {.noSideEffect,
rtl, extern: "nsuEndsWith".} = rtl, extern: "nsuEndsWith".} =
@ -1343,11 +1172,11 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect,
while i+j <% s.len: while i+j <% s.len:
if s[i+j] != suffix[i]: return false if s[i+j] != suffix[i]: return false
inc(i) inc(i)
if suffix[i] == '\0': return true if i >= suffix.len: return true
proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} = proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} =
## Returns true iff ``s`` ends with ``suffix``. ## 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, proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect,
rtl, extern: "nsuContinuesWith".} = rtl, extern: "nsuContinuesWith".} =
@ -1356,8 +1185,8 @@ proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect,
## If ``substr == ""`` true is returned. ## If ``substr == ""`` true is returned.
var i = 0 var i = 0
while true: while true:
if substr[i] == '\0': return true if i >= substr.len: return true
if s[i+start] != substr[i]: return false if i+start >= s.len or s[i+start] != substr[i]: return false
inc(i) inc(i)
proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0) 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`. ## If `last` is unspecified, it defaults to `s.high`.
## ##
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
if sub.len > s.len: if sub.len > s.len: return -1
return -1 if sub.len == 1: return find(s, sub[0], start, last)
if sub.len == 1:
return find(s, sub[0], start, last)
var a {.noinit.}: SkipTable var a {.noinit.}: SkipTable
initSkipTable(a, sub) initSkipTable(a, sub)
result = find(a, s, sub, start, last) 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 ## The original string is returned if `width` is less than or equal
## to `s.len`. ## to `s.len`.
if width <= s.len: if width <= s.len: return s
return s
result = newString(width) result = newString(width)
# Left padding will be one fillChar # Left padding will be one fillChar
# smaller if there are an odd number # smaller if there are an odd number
# of characters # of characters
let let
charsLeft = (width - s.len) charsLeft = (width - s.len)
leftPadding = charsLeft div 2 leftPadding = charsLeft div 2
for i in 0 ..< width: for i in 0 ..< width:
if i >= leftPadding and i < leftPadding + s.len: if i >= leftPadding and i < leftPadding + s.len:
# we are where the string should be located # 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 var i = 0
while true: while true:
i = s.find(sub, i) i = s.find(sub, i)
if i < 0: if i < 0: break
break if overlapping: inc i
if overlapping: else: i += sub.len
inc i
else:
i += sub.len
inc result inc result
proc count*(s: string, sub: char): int {.noSideEffect, proc count*(s: string, sub: char): int {.noSideEffect,
rtl, extern: "nsuCountChar".} = rtl, extern: "nsuCountChar".} =
## Count the occurrences of the character `sub` in the string `s`. ## Count the occurrences of the character `sub` in the string `s`.
for c in s: for c in s:
if c == sub: if c == sub: inc result
inc result
proc count*(s: string, subs: set[char]): int {.noSideEffect, proc count*(s: string, subs: set[char]): int {.noSideEffect,
rtl, extern: "nsuCountCharSet".} = rtl, extern: "nsuCountCharSet".} =
## Count the occurrences of the group of character `subs` in the string `s`. ## Count the occurrences of the group of character `subs` in the string `s`.
for c in s: for c in s:
if c in subs: if c in subs: inc result
inc result
proc quoteIfContainsWhite*(s: string): string {.deprecated.} = proc quoteIfContainsWhite*(s: string): string {.deprecated.} =
## Returns ``'"' & s & '"'`` if `s` contains a space and does not ## 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 ## **DEPRECATED** as it was confused for shell quoting function. For this
## application use `osproc.quoteShell <osproc.html#quoteShell>`_. ## application use `osproc.quoteShell <osproc.html#quoteShell>`_.
if find(s, {' ', '\t'}) >= 0 and s[0] != '"': if find(s, {' ', '\t'}) >= 0 and s[0] != '"': result = '"' & s & '"'
result = '"' & s & '"' else: result = s
else:
result = s
proc contains*(s: string, c: char): bool {.noSideEffect.} = proc contains*(s: string, c: char): bool {.noSideEffect.} =
## Same as ``find(s, c) >= 0``. ## 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 ## Same as replace, but specialized for doing multiple replacements in a single
## pass through the input string. ## pass through the input string.
## ##
## Calling replace multiple times after each other is inefficient and result in too many allocations ## multiReplace performs all replacements in a single pass, this means it can be used
## follwed by immediate deallocations as portions of the string gets replaced. ## to swap the occurences of "a" and "b", for instance.
## multiReplace performs all replacements in a single pass.
## ##
## If the resulting string is not longer than the original input string, only a single ## If the resulting string is not longer than the original input string, only a single
## memory allocation is required. ## memory allocation is required.
@ -1753,14 +1566,13 @@ proc parseOctInt*(s: string): int {.noSideEffect,
## of the following optional prefixes: ``0o``, ``0O``. Underscores within ## of the following optional prefixes: ``0o``, ``0O``. Underscores within
## `s` are ignored. ## `s` are ignored.
var i = 0 var i = 0
if s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
while true: while i < s.len:
case s[i] case s[i]
of '_': inc(i) of '_': inc(i)
of '0'..'7': of '0'..'7':
result = result shl 3 or (ord(s[i]) - ord('0')) result = result shl 3 or (ord(s[i]) - ord('0'))
inc(i) inc(i)
of '\0': break
else: raise newException(ValueError, "invalid integer: " & s) else: raise newException(ValueError, "invalid integer: " & s)
proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect, proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect,
@ -1854,11 +1666,13 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
var i = prefix.len var i = prefix.len
if not s.startsWith(prefix): if not s.startsWith(prefix):
raise newException(ValueError, raise newException(ValueError,
"String does not start with a prefix of: " & prefix) "String does not start with: " & prefix)
while true: while true:
if i == s.len-suffix.len: break if i >= s.len-suffix.len: break
case s[i] if s[i] == '\\':
of '\\': if i+1 >= s.len:
result.add('\\')
break
case s[i+1]: case s[i+1]:
of 'x': of 'x':
inc i, 2 inc i, 2
@ -1872,15 +1686,15 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
result.add('\'') result.add('\'')
of '\"': of '\"':
result.add('\"') result.add('\"')
else: result.add("\\" & s[i+1]) else:
inc(i) result.add("\\" & s[i+1])
of '\0': break inc(i, 2)
else: else:
result.add(s[i]) result.add(s[i])
inc(i) inc(i)
if not s.endsWith(suffix): if not s.endsWith(suffix):
raise newException(ValueError, raise newException(ValueError,
"String does not end with a suffix of: " & suffix) "String does not end in: " & suffix)
proc validIdentifier*(s: string): bool {.noSideEffect, proc validIdentifier*(s: string): bool {.noSideEffect,
rtl, extern: "nsuValidIdentifier".} = 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`. ## and is followed by any number of characters of the set `IdentChars`.
runnableExamples: runnableExamples:
doAssert "abc_def08".validIdentifier 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: for i in 1..s.len-1:
if s[i] notin IdentChars: return false if s[i] notin IdentChars: return false
return true return true
@ -1909,7 +1723,7 @@ proc editDistance*(a, b: string): int {.noSideEffect,
# strip common prefix: # strip common prefix:
var s = 0 var s = 0
while a[s] == b[s] and a[s] != '\0': while s < len1 and a[s] == b[s]:
inc(s) inc(s)
dec(len1) dec(len1)
dec(len2) dec(len2)
@ -1982,8 +1796,6 @@ proc editDistance*(a, b: string): int {.noSideEffect,
if x > c3: x = c3 if x > c3: x = c3
row[p] = x row[p] = x
result = row[e] result = row[e]
#dealloc(row)
# floating point formating: # floating point formating:
when not defined(js): when not defined(js):
@ -2310,9 +2122,8 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
var i = 0 var i = 0
var num = 0 var num = 0
while i < len(formatstr): while i < len(formatstr):
if formatstr[i] == '$': if formatstr[i] == '$' and i+1 < len(formatstr):
case formatstr[i+1] # again we use the fact that strings case formatstr[i+1]
# are zero-terminated here
of '#': of '#':
if num > a.high: invalidFormatString() if num > a.high: invalidFormatString()
add s, a[num] add s, a[num]
@ -2326,7 +2137,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
inc(i) # skip $ inc(i) # skip $
var negative = formatstr[i] == '-' var negative = formatstr[i] == '-'
if negative: inc 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') j = j * 10 + ord(formatstr[i]) - ord('0')
inc(i) inc(i)
let idx = if not negative: j-1 else: a.len-j 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] == '-' var negative = formatstr[j] == '-'
if negative: inc j if negative: inc j
var isNumber = 0 var isNumber = 0
while formatstr[j] notin {'\0', '}'}: while j < formatstr.len and formatstr[j] notin {'\0', '}'}:
if formatstr[j] in Digits: if formatstr[j] in Digits:
k = k * 10 + ord(formatstr[j]) - ord('0') k = k * 10 + ord(formatstr[j]) - ord('0')
if isNumber == 0: isNumber = 1 if isNumber == 0: isNumber = 1
@ -2356,7 +2167,7 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
i = j+1 i = j+1
of 'a'..'z', 'A'..'Z', '\128'..'\255', '_': of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
var j = i+1 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) var x = findNormalized(substr(formatstr, i+1, j-1), a)
if x >= 0 and x < high(a): add s, a[x+1] if x >= 0 and x < high(a): add s, a[x+1]
else: invalidFormatString() else: invalidFormatString()
@ -2628,13 +2439,7 @@ when isMainModule:
doAssert isSpaceAscii(" ") doAssert isSpaceAscii(" ")
doAssert(not isSpaceAscii("ABc \td")) doAssert(not isSpaceAscii("ABc \td"))
doAssert(isNilOrEmpty(""))
doAssert(isNilOrEmpty(nil))
doAssert(not isNilOrEmpty("test"))
doAssert(not isNilOrEmpty(" "))
doAssert(isNilOrWhitespace("")) doAssert(isNilOrWhitespace(""))
doAssert(isNilOrWhitespace(nil))
doAssert(isNilOrWhitespace(" ")) doAssert(isNilOrWhitespace(" "))
doAssert(isNilOrWhitespace("\t\l \v\r\f")) doAssert(isNilOrWhitespace("\t\l \v\r\f"))
doAssert(not isNilOrWhitespace("ABc \td")) doAssert(not isNilOrWhitespace("ABc \td"))

View file

@ -1318,24 +1318,23 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}=
result = "" result = ""
var i = 0 var i = 0
var currentF = "" var currentF = ""
while true: while i < f.len:
case f[i] case f[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',':
formatToken(dt, currentF, result) formatToken(dt, currentF, result)
currentF = "" currentF = ""
if f[i] == '\0': break
if f[i] == '\'': if f[i] == '\'':
inc(i) # Skip ' inc(i) # Skip '
while f[i] != '\'' and f.len-1 > i: while i < f.len-1 and f[i] != '\'':
result.add(f[i]) result.add(f[i])
inc(i) inc(i)
else: result.add(f[i]) else: result.add(f[i])
else: else:
# Check if the letter being added matches previous accumulated buffer. # 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]) currentF.add(f[i])
else: else:
formatToken(dt, currentF, result) formatToken(dt, currentF, result)
@ -1343,6 +1342,7 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}=
currentF = "" currentF = ""
inc(i) inc(i)
formatToken(dt, currentF, result)
proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} = proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
## Converts a `DateTime` object to a string representation. ## Converts a `DateTime` object to a string representation.
@ -1521,44 +1521,47 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
j += 4 j += 4
of "z": of "z":
dt.isDst = false 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 dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour
elif value[j] == '-': elif ch == '-':
dt.utcOffset = parseInt($value[j+1]) * secondsInHour dt.utcOffset = parseInt($value[j+1]) * secondsInHour
elif value[j] == 'Z': elif ch == 'Z':
dt.utcOffset = 0 dt.utcOffset = 0
j += 1 j += 1
return return
else: else:
raise newException(ValueError, raise newException(ValueError,
"Couldn't parse timezone offset (z), got: " & value[j]) "Couldn't parse timezone offset (z), got: " & ch)
j += 2 j += 2
of "zz": of "zz":
dt.isDst = false 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 dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour
elif value[j] == '-': elif ch == '-':
dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour
elif value[j] == 'Z': elif ch == 'Z':
dt.utcOffset = 0 dt.utcOffset = 0
j += 1 j += 1
return return
else: else:
raise newException(ValueError, raise newException(ValueError,
"Couldn't parse timezone offset (zz), got: " & value[j]) "Couldn't parse timezone offset (zz), got: " & ch)
j += 3 j += 3
of "zzz": of "zzz":
dt.isDst = false dt.isDst = false
var factor = 0 var factor = 0
if value[j] == '+': factor = -1 let ch = if j < value.len: value[j] else: '\0'
elif value[j] == '-': factor = 1 if ch == '+': factor = -1
elif value[j] == 'Z': elif ch == '-': factor = 1
elif ch == 'Z':
dt.utcOffset = 0 dt.utcOffset = 0
j += 1 j += 1
return return
else: else:
raise newException(ValueError, 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 dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour
j += 4 j += 4
dt.utcOffset += factor * value[j..j+1].parseInt() * 60 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.nanosecond = 0
dt.isDst = true # using this is flag for checking whether a timezone has \ 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) # been read (because DST is always false when a tz is parsed)
while true: while i < layout.len:
case layout[i] case layout[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',': of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',':
if token.len > 0: if token.len > 0:
parseToken(dt, token, value, j) parseToken(dt, token, value, j)
# Reset token # Reset token
token = "" token = ""
# Break if at end of line
if layout[i] == '\0': break
# Skip separator and everything between single quotes # Skip separator and everything between single quotes
# These are literals in both the layout and the value string # These are literals in both the layout and the value string
if layout[i] == '\'': if layout[i] == '\'':
inc(i) inc(i)
while layout[i] != '\'' and layout.len-1 > i: while i < layout.len-1 and layout[i] != '\'':
inc(i) inc(i)
inc(j) inc(j)
inc(i) inc(i)
@ -1642,13 +1643,15 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime =
inc(j) inc(j)
else: else:
# Check if the letter being added matches previous accumulated buffer. # 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]) token.add(layout[i])
inc(i) inc(i)
else: else:
parseToken(dt, token, value, j) parseToken(dt, token, value, j)
token = "" token = ""
if i >= layout.len and token.len > 0:
parseToken(dt, token, value, j)
if dt.isDst: if dt.isDst:
# No timezone parsed - assume timezone is `zone` # No timezone parsed - assume timezone is `zone`
result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone) result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)

View file

@ -94,7 +94,7 @@ proc parseAuthority(authority: string, result: var Uri) =
var i = 0 var i = 0
var inPort = false var inPort = false
var inIPv6 = false var inIPv6 = false
while true: while i < authority.len:
case authority[i] case authority[i]
of '@': of '@':
swap result.password, result.port swap result.password, result.port
@ -111,7 +111,6 @@ proc parseAuthority(authority: string, result: var Uri) =
inIPv6 = true inIPv6 = true
of ']': of ']':
inIPv6 = false inIPv6 = false
of '\0': break
else: else:
if inPort: if inPort:
result.port.add(authority[i]) result.port.add(authority[i])
@ -128,11 +127,11 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
parseAuthority(result.path, result) parseAuthority(result.path, result)
result.path.setLen(0) result.path.setLen(0)
if uri[i] == '?': if i < uri.len and uri[i] == '?':
i.inc # Skip '?' i.inc # Skip '?'
i.inc parseUntil(uri, result.query, {'#'}, i) i.inc parseUntil(uri, result.query, {'#'}, i)
if uri[i] == '#': if i < uri.len and uri[i] == '#':
i.inc # Skip '#' i.inc # Skip '#'
i.inc parseUntil(uri, result.anchor, {}, i) 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) # Check if this is a reference URI (relative URI)
let doubleSlash = uri.len > 1 and uri[1] == '/' 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 '//'. # Make sure ``uri`` doesn't begin with '//'.
if not doubleSlash: if not doubleSlash:
parsePath(uri, i, result) parsePath(uri, i, result)
@ -164,7 +163,7 @@ proc parseUri*(uri: string, result: var Uri) =
# Scheme # Scheme
i.inc parseWhile(uri, result.scheme, Letters + Digits + {'+', '-', '.'}, i) 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) # Assume this is a reference URI (relative URI)
i = 0 i = 0
result.scheme.setLen(0) result.scheme.setLen(0)
@ -174,7 +173,7 @@ proc parseUri*(uri: string, result: var Uri) =
i.inc # Skip ':' i.inc # Skip ':'
# Authority # Authority
if uri[i] == '/' and uri[i+1] == '/': if i+1 < uri.len and uri[i] == '/' and uri[i+1] == '/':
i.inc(2) # Skip // i.inc(2) # Skip //
var authority = "" var authority = ""
i.inc parseUntil(uri, authority, {'/', '?', '#'}, i) i.inc parseUntil(uri, authority, {'/', '?', '#'}, i)
@ -197,13 +196,13 @@ proc removeDotSegments(path: string): string =
let endsWithSlash = path[path.len-1] == '/' let endsWithSlash = path[path.len-1] == '/'
var i = 0 var i = 0
var currentSegment = "" var currentSegment = ""
while true: while i < path.len:
case path[i] case path[i]
of '/': of '/':
collection.add(currentSegment) collection.add(currentSegment)
currentSegment = "" currentSegment = ""
of '.': 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: if collection.len > 0:
discard collection.pop() discard collection.pop()
i.inc 3 i.inc 3
@ -212,13 +211,11 @@ proc removeDotSegments(path: string): string =
i.inc 2 i.inc 2
continue continue
currentSegment.add path[i] currentSegment.add path[i]
of '\0':
if currentSegment != "":
collection.add currentSegment
break
else: else:
currentSegment.add path[i] currentSegment.add path[i]
i.inc i.inc
if currentSegment != "":
collection.add currentSegment
result = collection.join("/") result = collection.join("/")
if endsWithSlash: result.add '/' if endsWithSlash: result.add '/'
@ -320,18 +317,18 @@ proc `/`*(x: Uri, path: string): Uri =
result = x result = x
if result.path.len == 0: if result.path.len == 0:
if path[0] != '/': if path.len == 0 or path[0] != '/':
result.path = "/" result.path = "/"
result.path.add(path) result.path.add(path)
return return
if result.path[result.path.len-1] == '/': if result.path.len > 0 and result.path[result.path.len-1] == '/':
if path[0] == '/': if path.len > 0 and path[0] == '/':
result.path.add(path[1 .. path.len-1]) result.path.add(path[1 .. path.len-1])
else: else:
result.path.add(path) result.path.add(path)
else: else:
if path[0] != '/': if path.len == 0 or path[0] != '/':
result.path.add '/' result.path.add '/'
result.path.add(path) result.path.add(path)

View file

@ -232,7 +232,7 @@ proc createAttributeNS*(doc: PDocument, namespaceURI: string, qualifiedName: str
raise newException(EInvalidCharacterErr, "Invalid character") raise newException(EInvalidCharacterErr, "Invalid character")
# Exceptions # Exceptions
if qualifiedName.contains(':'): if qualifiedName.contains(':'):
let qfnamespaces = qualifiedName.toLower().split(':') let qfnamespaces = qualifiedName.toLowerAscii().split(':')
if isNil(namespaceURI): if isNil(namespaceURI):
raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil") raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
elif qfnamespaces[0] == "xml" and elif qfnamespaces[0] == "xml" and
@ -311,7 +311,7 @@ proc createElement*(doc: PDocument, tagName: string): PElement =
proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PElement = proc createElementNS*(doc: PDocument, namespaceURI: string, qualifiedName: string): PElement =
## Creates an element of the given qualified name and namespace URI. ## Creates an element of the given qualified name and namespace URI.
if qualifiedName.contains(':'): if qualifiedName.contains(':'):
let qfnamespaces = qualifiedName.toLower().split(':') let qfnamespaces = qualifiedName.toLowerAscii().split(':')
if isNil(namespaceURI): if isNil(namespaceURI):
raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil") raise newException(ENamespaceErr, "When qualifiedName contains a prefix namespaceURI cannot be nil")
elif qfnamespaces[0] == "xml" and elif qfnamespaces[0] == "xml" and
@ -533,13 +533,13 @@ proc `prefix=`*(n: PNode, value: string) =
if isNil(n.fNamespaceURI): if isNil(n.fNamespaceURI):
raise newException(ENamespaceErr, "namespaceURI cannot be nil") 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, raise newException(ENamespaceErr,
"When the namespace prefix is \"xml\" namespaceURI has to be \"http://www.w3.org/XML/1998/namespace\"") "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, raise newException(ENamespaceErr,
"When the namespace prefix is \"xmlns\" namespaceURI has to be \"http://www.w3.org/2000/xmlns/\"") "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\"") raise newException(ENamespaceErr, "An AttributeNode cannot have a prefix of \"xmlns\"")
n.fNodeName = value & ":" & n.fLocalName n.fNodeName = value & ":" & n.fLocalName

View file

@ -1280,15 +1280,13 @@ proc setLen*[T](s: var seq[T], newlen: Natural) {.
## sets the length of `s` to `newlen`. ## sets the length of `s` to `newlen`.
## ``T`` may be any sequence type. ## ``T`` may be any sequence type.
## If the current length is greater than the new length, ## If the current length is greater than the new length,
## ``s`` will be truncated. `s` cannot be nil! To initialize a sequence with ## ``s`` will be truncated.
## a size, use ``newSeq`` instead.
proc setLen*(s: var string, newlen: Natural) {. proc setLen*(s: var string, newlen: Natural) {.
magic: "SetLengthStr", noSideEffect.} magic: "SetLengthStr", noSideEffect.}
## sets the length of `s` to `newlen`. ## sets the length of `s` to `newlen`.
## If the current length is greater than the new length, ## If the current length is greater than the new length,
## ``s`` will be truncated. `s` cannot be nil! To initialize a string with ## ``s`` will be truncated.
## a size, use ``newString`` instead.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## var myS = "Nim is great!!" ## var myS = "Nim is great!!"
@ -2414,6 +2412,7 @@ proc `==` *[T](x, y: seq[T]): bool {.noSideEffect.} =
if seqToPtr(x) == seqToPtr(y): if seqToPtr(x) == seqToPtr(y):
return true return true
when not defined(nimNoNil):
if x.isNil or y.isNil: if x.isNil or y.isNil:
return false return false
@ -3224,7 +3223,7 @@ when not defined(JS): #and not defined(nimscript):
when declared(initGC): initGC() when declared(initGC): initGC()
when not defined(nimscript): 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 ## allows you to override the behaviour of your application when CTRL+C
## is pressed. Only one such hook is supported. ## is pressed. Only one such hook is supported.
@ -4012,18 +4011,18 @@ proc addQuoted*[T](s: var string, x: T) =
when hasAlloc: when hasAlloc:
# XXX: make these the default (or implement the NilObject optimization) # 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, ## Adds ``y`` to ``x`` unless ``x`` is not yet initialized; in that case,
## ``x`` becomes ``@[y]`` ## ``x`` becomes ``@[y]``
if x == nil: x = @[y] if x == nil: x = @[y]
else: x.add(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 ``""`` ## Adds ``y`` to ``x``. If ``x`` is ``nil`` it is initialized to ``""``
if x == nil: x = "" if x == nil: x = ""
x.add(y) 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 ## Adds ``y`` to ``x`` unless ``x`` is not yet initalized; in that
## case, ``x`` becomes ``y`` ## case, ``x`` becomes ``y``
if x == nil: x = y if x == nil: x = y

View file

@ -40,4 +40,4 @@ proc reraiseException() {.compilerRtl.} =
proc writeStackTrace() = discard proc writeStackTrace() = discard
proc setControlCHook(hook: proc () {.noconv.} not nil) = discard proc setControlCHook(hook: proc () {.noconv.}) = discard

View file

@ -503,7 +503,7 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
registerSignalHandler() # call it in initialization section 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: # ugly cast, but should work on all architectures:
type SignalHandler = proc (sign: cint) {.noconv, benign.} type SignalHandler = proc (sign: cint) {.noconv, benign.}
c_signal(SIGINT, cast[SignalHandler](hook)) c_signal(SIGINT, cast[SignalHandler](hook))

View file

@ -22,21 +22,34 @@ proc resize(old: int): int {.inline.} =
proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} = proc cmpStrings(a, b: NimString): int {.inline, compilerProc.} =
if a == b: return 0 if a == b: return 0
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 a == nil: return -1
if b == nil: return 1 if b == nil: return 1
let minlen = min(a.len, b.len) let alen = a.len
let blen = b.len
let minlen = min(alen, blen)
if minlen > 0: if minlen > 0:
result = c_memcmp(addr a.data, addr b.data, minlen.csize) result = c_memcmp(addr a.data, addr b.data, minlen.csize)
if result == 0: if result == 0:
result = a.len - b.len result = alen - blen
else: else:
result = a.len - b.len result = alen - blen
proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} = proc eqStrings(a, b: NimString): bool {.inline, compilerProc.} =
if a == b: return true if a == b: return true
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 if a == nil or b == nil: return false
return a.len == b.len and let alen = a.len
equalMem(addr(a.data), addr(b.data), 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): when declared(allocAtomic):
template allocStr(size: untyped): untyped = template allocStr(size: untyped): untyped =
@ -101,9 +114,6 @@ proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
if str == nil: NimString(nil) if str == nil: NimString(nil)
else: toNimStr(str, str.len) else: toNimStr(str, str.len)
template wasMoved(x: NimString): bool = false
# (x.reserved and seqShallowFlag) != 0
proc copyString(src: NimString): NimString {.compilerRtl.} = proc copyString(src: NimString): NimString {.compilerRtl.} =
if src != nil: if src != nil:
if (src.reserved and seqShallowFlag) != 0: if (src.reserved and seqShallowFlag) != 0:
@ -161,14 +171,16 @@ proc hashString(s: string): int {.compilerproc.} =
proc addChar(s: NimString, c: char): NimString = proc addChar(s: NimString, c: char): NimString =
# is compilerproc! # is compilerproc!
if s == nil:
result = rawNewStringNoInit(1)
result.len = 0
else:
result = s result = s
if result.len >= result.space: if result.len >= result.space:
let r = resize(result.space) let r = resize(result.space)
result = cast[NimString](growObj(result, result = cast[NimString](growObj(result,
sizeof(TGenericSeq) + r + 1)) sizeof(TGenericSeq) + r + 1))
result.reserved = r result.reserved = r
elif wasMoved(s):
result = newOwnedString(s, s.len)
result.data[result.len] = c result.data[result.len] = c
result.data[result.len+1] = '\0' result.data[result.len+1] = '\0'
inc(result.len) inc(result.len)
@ -205,7 +217,9 @@ proc addChar(s: NimString, c: char): NimString =
# s = rawNewString(0); # s = rawNewString(0);
proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} = 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 result = dest
else: # slow path: else: # slow path:
var sp = max(resize(dest.space), dest.len + addlen) var sp = max(resize(dest.space), dest.len + addlen)
@ -216,6 +230,7 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
# DO NOT UPDATE LEN YET: dest.len = newLen # DO NOT UPDATE LEN YET: dest.len = newLen
proc appendString(dest, src: NimString) {.compilerproc, inline.} = proc appendString(dest, src: NimString) {.compilerproc, inline.} =
if src != nil:
copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1) copyMem(addr(dest.data[dest.len]), addr(src.data), src.len + 1)
inc(dest.len, src.len) inc(dest.len, src.len)
@ -226,8 +241,8 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} = proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
var n = max(newLen, 0) var n = max(newLen, 0)
if wasMoved(s): if s == nil:
result = newOwnedString(s, n) result = mnewString(newLen)
elif n <= s.space: elif n <= s.space:
result = s result = s
else: else:
@ -261,6 +276,18 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
GenericSeqSize)) GenericSeqSize))
result.reserved = r 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 {. proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
compilerRtl, inl.} = compilerRtl, inl.} =
result = seq result = seq
@ -301,6 +328,13 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
(newLen*%elemSize)), (result.len-%newLen) *% elemSize) (newLen*%elemSize)), (result.len-%newLen) *% elemSize)
result.len = newLen 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 ---------------------------------- # --------------- other string routines ----------------------------------
proc add*(result: var string; x: int64) = proc add*(result: var string; x: int64) =
let base = result.len let base = result.len

View file

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

View file

@ -0,0 +1,17 @@
discard """
file: "tarraycons_ptr_generic2.nim"
line: 17
errormsg: "type mismatch: got <ptr Hard[system.string]> 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]

View file

@ -1,7 +1,7 @@
# bug 2007 # bug 2007
import asyncdispatch, asyncnet, logging, json, uri, strutils, future import asyncdispatch, asyncnet, logging, json, uri, strutils, sugar
type type
Builder = ref object Builder = ref object
@ -27,7 +27,7 @@ proc newBuild*(onProgress: ProgressCB): Build =
result.onProgress = onProgress result.onProgress = onProgress
proc start(build: Build, repo, hash: string) {.async.} = 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.} = proc onProgress(builder: Builder, message: string) {.async.} =
debug($message) debug($message)

View file

@ -1,6 +1,6 @@
# bug #3313 # bug #3313
import unittest, future import unittest, sugar
{.experimental: "notnil".}
type type
ListNodeKind = enum ListNodeKind = enum
lnkNil, lnkCons lnkNil, lnkCons

View file

@ -31,7 +31,7 @@ type
s.pop() is T s.pop() is T
type ValueType = T type ValueType = T
const ValueTypeName = T.name.toUpper const ValueTypeName = T.name.toUpperAscii
proc genericAlgorithm[T](s: var Stack[T], y: T) = proc genericAlgorithm[T](s: var Stack[T], y: T) =
static: static:

View file

@ -3,12 +3,12 @@ template accept(x) =
template reject(x) = template reject(x) =
static: assert(not compiles(x)) static: assert(not compiles(x))
{.experimental: "notnil".}
type type
TRefObj = ref object TRefObj = ref object
x: int x: int
THasNotNils = object of TObject THasNotNils = object of RootObj
a: TRefObj not nil a: TRefObj not nil
b: TRefObj not nil b: TRefObj not nil
c: TRefObj c: TRefObj

View file

@ -15,6 +15,6 @@ type TFoo = object
converter toPtr*(some: var TFoo): ptr TFoo = (addr some) converter toPtr*(some: var TFoo): ptr TFoo = (addr some)
proc zoot(x: ptr TFoo) = nil proc zoot(x: ptr TFoo) = discard
var x: Tfoo var x: Tfoo
zoot(x) zoot(x)

View file

@ -1,15 +1,11 @@
discard """ discard """
file: "tnil.nim" file: "tnil.nim"
output: '''0x1 output: '''1
0
nil 0
nil
''' '''
disabled: "windows"
""" """
{.experimental: "notnil".}
type type
MyPointer = distinct pointer MyPointer = distinct pointer
MyString = distinct string MyString = distinct string
@ -17,7 +13,8 @@ type
MyInt = distinct int MyInt = distinct int
proc foo(a: MyPointer) = proc foo(a: MyPointer) =
echo a.repr # workaround a Windows 'repr' difference:
echo cast[int](a)
foo(cast[MyPointer](1)) foo(cast[MyPointer](1))
foo(cast[MyPointer](nil)) foo(cast[MyPointer](nil))

View file

@ -12,7 +12,7 @@ type
EIO2 = ref object of EIO EIO2 = ref object of EIO
proc q() {.tags: [FIO].} = proc q() {.tags: [FIO].} =
nil discard
proc raiser(): int = proc raiser(): int =
writeLine stdout, "arg" writeLine stdout, "arg"

View file

@ -3,7 +3,7 @@ discard """
exitcode: 1 exitcode: 1
""" """
proc returnsNil(): string = return nil proc returnsNil(): ref int = return nil
iterator fields*(a, b: int): int = iterator fields*(a, b: int): int =
if a == b: if a == b:
@ -17,6 +17,6 @@ proc main(): string =
result = "" result = ""
for i in fields(0, 1): for i in fields(0, 1):
let x = returnsNil() let x = returnsNil()
result &= "string literal " & $x result &= "string literal " & $x[]
echo main() echo main()

View file

@ -209,7 +209,7 @@ proc `$`*(data: Tcommandline_results): string =
# - Parse code # - 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. ## 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 ## 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 ## Pass in the string of the parameter triggering the call. If the
if not custom_validator.isNil: if not custom_validator.isNil:
except: try:
raise_or_quit(ValueError, ("Couldn't run custom proc for " &
"parameter $1:\n$2" % [escape(parameter),
getCurrentExceptionMsg()]))
let message = custom_validator(parameter, parsed_parameter) let message = custom_validator(parameter, parsed_parameter)
if not message.isNil and message.len > 0: if not message.isNil and message.len > 0:
raise_or_quit(ValueError, ("Failed to validate value for " & raise_or_quit(ValueError, ("Failed to validate value for " &
"parameter $1:\n$2" % [escape(parameter), message])) "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()]))
proc parse_parameter(quit_on_failure: bool, param, value: string, proc parse_parameter(quit_on_failure: bool, param, value: string,
param_kind: Tparam_kind): Tparsed_parameter = param_kind: Tparam_kind): Tparsed_parameter =

View file

@ -388,13 +388,13 @@ type
cdecl.} cdecl.}
##cp property emulators ##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.} = proc `get procName`*(obj: otype): memberType {.cdecl.} =
return obj.memberName 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.} = proc `set procName`*(obj: otype, value: memberType) {.cdecl.} =
obj.memberName = value 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) defGetter(otype, memberType, memberName, procName)
defSetter(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. #/ 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); #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 var name1, name2: PShape
getShapes(arb, name1, name2) 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. #/ 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); #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 var name1, name2: PBOdy
getBodies(arb, name1, name2) getBodies(arb, name1, name2)
@ -947,11 +947,11 @@ proc getDepth*(arb: PArbiter; i: cint): CpFloat {.
cdecl, importc: "cpArbiterGetDepth", dynlib: Lib.} cdecl, importc: "cpArbiterGetDepth", dynlib: Lib.}
##Shapes ##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.} = proc `set procName`*(obj: PShape, value: memberType) {.cdecl.} =
if activates and obj.body != nil: obj.body.activate() if activates and obj.body != nil: obj.body.activate()
obj.memberName = value 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) defGetter(PShape, memberType, memberName, procName)
defShapeSetter(memberType, memberName, procName, activates) defShapeSetter(memberType, memberName, procName, activates)
@ -1272,11 +1272,11 @@ proc activateBodies(constraint: PConstraint) {.inline.} =
# cpConstraintActivateBodies(constraint); \ # cpConstraintActivateBodies(constraint); \
# constraint->member = value; \ # 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.} = proc `set name`*(constraint: PConstraint, value: memberType) {.cdecl.} =
activateBodies(constraint) activateBodies(constraint)
constraint.member = value 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) defGetter(PConstraint, memberType, member, name)
defConstraintSetter(memberType, member, name) defConstraintSetter(memberType, member, name)
# CP_DefineConstraintStructGetter(cpSpace*, CP_PRIVATE(space), Space) # CP_DefineConstraintStructGetter(cpSpace*, CP_PRIVATE(space), Space)
@ -1306,18 +1306,18 @@ proc getImpulse*(constraint: PConstraint): CpFloat {.inline.} =
# cpConstraintActivateBodies(constraint); \ # cpConstraintActivateBodies(constraint); \
# ((struct *)constraint)->member = value; \ # ((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") 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.} = proc `get ctype name`*(constraint: PConstraint): memberType {.cdecl.} =
constraintCheckCast(constraint, ctype) constraintCheckCast(constraint, ctype)
result = cast[`P ctype`](constraint).member 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.} = proc `set ctype name`*(constraint: PConstraint, value: memberType) {.cdecl.} =
constraintCheckCast(constraint, ctype) constraintCheckCast(constraint, ctype)
activateBodies(constraint) activateBodies(constraint)
cast[`P ctype`](constraint).member = value 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) defCGetter(ctype, memberType, member, name)
defCSetter(ctype, memberType, member, name) defCSetter(ctype, memberType, member, name)

View file

@ -24,7 +24,7 @@ const
ENET_VERSION_MAJOR* = 1 ENET_VERSION_MAJOR* = 1
ENET_VERSION_MINOR* = 3 ENET_VERSION_MINOR* = 3
ENET_VERSION_PATCH* = 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)) (((major) shl 16) or ((minor) shl 8) or (patch))
const const
@ -277,22 +277,22 @@ when defined(Linux) or true:
dataLength*: csize dataLength*: csize
TENetSocketSet* = Tfd_set TENetSocketSet* = Tfd_set
## see if these are different on win32, if not then get rid of these ## 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)) (htons(value))
template ENET_HOST_TO_NET_32*(value: expr): expr = template ENET_HOST_TO_NET_32*(value: untyped): untyped =
(htonl(value)) (htonl(value))
template ENET_NET_TO_HOST_16*(value: expr): expr = template ENET_NET_TO_HOST_16*(value: untyped): untyped =
(ntohs(value)) (ntohs(value))
template ENET_NET_TO_HOST_32*(value: expr): expr = template ENET_NET_TO_HOST_32*(value: untyped): untyped =
(ntohl(value)) (ntohl(value))
template ENET_SOCKETSET_EMPTY*(sockset: expr): expr = template ENET_SOCKETSET_EMPTY*(sockset: untyped): untyped =
FD_ZERO(addr((sockset))) 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))) 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))) 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))) FD_ISSET(socket, addr((sockset)))
when defined(Windows): when defined(Windows):
@ -606,7 +606,7 @@ proc protocolCommandSize*(commandNumber: cuchar): csize{.
{.pop.} {.pop.}
from hashes import `!$`, `!&`, THash, hash from hashes import `!$`, `!&`, Hash, hash
proc hash*(x: TAddress): THash {.nimcall, noSideEffect.} = proc hash*(x: TAddress): Hash {.nimcall, noSideEffect.} =
result = !$(hash(x.host.int32) !& hash(x.port.int16)) result = !$(hash(x.host.int32) !& hash(x.port.int16))

View file

@ -1,15 +1,15 @@
import macros, macro_dsl, estreams import macros, macro_dsl, estreams
from strutils import format from strutils import format
template newLenName(): stmt {.immediate.} = template newLenName() =
let lenName {.inject.} = ^("len"& $lenNames) let lenName {.inject.} = ^("len"& $lenNames)
inc(lenNames) inc(lenNames)
template defPacketImports*(): stmt {.immediate, dirty.} = template defPacketImports*() {.dirty.} =
import macros, macro_dsl, estreams import macros, macro_dsl, estreams
from strutils import format from strutils import format
macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} = macro defPacket*(typeNameN: untyped, typeFields: untyped): untyped =
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
let let
typeName = quoted2ident(typeNameN) typeName = quoted2ident(typeNameN)
@ -60,7 +60,7 @@ macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
let let
name = typeFields[i][0] name = typeFields[i][0]
dotName = packetID.dot(name) dotName = packetID.dot(name)
resName = newIdentNode(!"result").dot(name) resName = newIdentNode("result").dot(name)
case typeFields[i][1].kind case typeFields[i][1].kind
of nnkBracketExpr: #ex: paddedstring[32, '\0'], array[range, type] of nnkBracketExpr: #ex: paddedstring[32, '\0'], array[range, type]
case $typeFields[i][1][0].ident case $typeFields[i][1][0].ident
@ -200,7 +200,7 @@ proc iddefs*(a: string; b: NimNode): NimNode {.compileTime.} =
proc varTy*(a: NimNode): NimNode {.compileTime.} = proc varTy*(a: NimNode): NimNode {.compileTime.} =
result = newNimNode(nnkVarTy).und(a) result = newNimNode(nnkVarTy).und(a)
macro forwardPacket*(typeName: expr, underlyingType: expr): stmt {.immediate.} = macro forwardPacket*(typeName: untyped, underlyingType: untyped): untyped =
var var
packetID = ^"p" packetID = ^"p"
streamID = ^"s" streamID = ^"s"
@ -234,7 +234,7 @@ macro forwardPacket*(typeName: expr, underlyingType: expr): stmt {.immediate.} =
echo "unknown type:", repr(underlyingtype) echo "unknown type:", repr(underlyingtype)
echo(repr(result)) 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 = proc `read typeName`*(buffer: PBuffer): typeName =
#discard readData(s, addr result, sizeof(result)) #discard readData(s, addr result, sizeof(result))
var res: underlyingType var res: underlyingType

View file

@ -10,7 +10,7 @@ proc und*(a: NimNode; b: varargs[NimNode]): NimNode {.compileTime.} =
proc `^`*(a: string): NimNode {.compileTime.} = proc `^`*(a: string): NimNode {.compileTime.} =
## new ident node ## new ident node
result = newIdentNode(!a) result = newIdentNode(a)
proc `[]`*(a, b: NimNode): NimNode {.compileTime.} = proc `[]`*(a, b: NimNode): NimNode {.compileTime.} =
## new bracket expression: node[node] not to be confused with node[indx] ## new bracket expression: node[node] not to be confused with node[indx]
result = newNimNode(nnkBracketExpr).und(a, b) result = newNimNode(nnkBracketExpr).und(a, b)
@ -34,7 +34,7 @@ proc emptyNode*(): NimNode {.compileTime.} =
proc dot*(left, right: NimNode): NimNode {.compileTime.} = proc dot*(left, right: NimNode): NimNode {.compileTime.} =
result = newNimNode(nnkDotExpr).und(left, right) result = newNimNode(nnkDotExpr).und(left, right)
proc prefix*(a: string, b: NimNode): NimNode {.compileTime.} = 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.} = proc quoted2ident*(a: NimNode): NimNode {.compileTime.} =
if a.kind != nnkAccQuoted: if a.kind != nnkAccQuoted:
@ -45,13 +45,13 @@ proc quoted2ident*(a: NimNode): NimNode {.compileTime.} =
result = ^pname result = ^pname
macro `?`(a: expr): expr = macro `?`(a: untyped): untyped =
## Character literal ?A #=> 'A' ## Character literal ?A #=> 'A'
result = ($a[1].ident)[0].lit result = ($a[1].ident)[0].lit
## echo(?F,?a,?t,?t,?y) ## echo(?F,?a,?t,?t,?y)
when isMainModule: when isMainModule:
macro foo(x: stmt): stmt = macro foo(x: untyped) =
result = newNimNode(nnkStmtList) result = newNimNode(nnkStmtList)
result.add(newNimNode(nnkCall).und(!!"echo", "Hello thar".lit)) result.add(newNimNode(nnkCall).und(!!"echo", "Hello thar".lit))
result.add(newCall("echo", lit("3 * 45 = "), (3.lit.infix("*", 45.lit)))) result.add(newCall("echo", lit("3 * 45 = "), (3.lit.infix("*", 45.lit))))

View file

@ -7,7 +7,7 @@ task "install", "compile and install nake binary":
for index, dir in pairs(path): for index, dir in pairs(path):
echo " ", index, ". ", dir echo " ", index, ". ", dir
echo "Where to install nake binary? (quit with ^C or quit or exit)" 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 var index = 0
case ans case ans
of "q", "quit", "x", "exit": of "q", "quit", "x", "exit":

View file

@ -78,7 +78,7 @@ proc write*(buffer: PBuffer; val: var string) =
setLen buffer.data, buffer.pos + length.int setLen buffer.data, buffer.pos + length.int
copyMem(addr buffer.data[buffer.pos], addr val[0], length.int) copyMem(addr buffer.data[buffer.pos], addr val[0], length.int)
inc buffer.pos, 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 var v: T
shallowCopy v, val shallowCopy v, val
writeBE buffer, v writeBE buffer, v

View file

@ -5,8 +5,8 @@ type
TInputFinishedProc* = proc() TInputFinishedProc* = proc()
TKeyCallback = proc() TKeyCallback = proc()
PKeyClient* = ref object PKeyClient* = ref object
onKeyDown: TTable[int32, TKeyCallback] onKeyDown: Table[int32, TKeyCallback]
onKeyUp: TTable[int32, TKeyCallback] onKeyUp: Table[int32, TKeyCallback]
name: string name: string
PTextInput* = ref object PTextInput* = ref object
text*: string text*: string
@ -134,5 +134,5 @@ iterator pollEvents*(window: PRenderWindow): PEvent =
of EvtMouseButtonReleased: addButtonEvent(event.mouseButton.button, up) of EvtMouseButtonReleased: addButtonEvent(event.mouseButton.button, up)
of EvtTextEntered: recordText(activeInput, event.text) of EvtTextEntered: recordText(activeInput, event.text)
of EvtMouseMoved: setMousePos(event.mouseMove.x, event.mouseMove.y) of EvtMouseMoved: setMousePos(event.mouseMove.x, event.mouseMove.y)
else: nil else: discard
yield(addr event) yield(addr event)

View file

@ -59,7 +59,7 @@ type
of Projectile: of Projectile:
bullet*: PBulletRecord bullet*: PBulletRecord
else: else:
nil discard
PBulletRecord* = ref TBulletRecord PBulletRecord* = ref TBulletRecord
TBulletRecord* = object TBulletRecord* = object
id*: int16 id*: int16
@ -115,10 +115,10 @@ var
cfg: PZoneSettings cfg: PZoneSettings
SpriteSheets* = initTable[string, PSpriteSheet](64) SpriteSheets* = initTable[string, PSpriteSheet](64)
SoundCache * = initTable[string, PSoundRecord](64) SoundCache * = initTable[string, PSoundRecord](64)
nameToVehID*: TTable[string, int] nameToVehID*: Table[string, int]
nameToItemID*: TTable[string, int] nameToItemID*: Table[string, int]
nameToObjID*: TTable[string, int] nameToObjID*: Table[string, int]
nameToBulletID*: TTable[string, int] nameToBulletID*: Table[string, int]
activeState = Lobby activeState = Lobby
proc newSprite*(filename: string; errors: var seq[string]): PSpriteSheet 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 newSound*(filename: string; errors: var seq[string]): PSoundRecord
proc load*(s: PSoundRecord): bool {.discardable.} 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 loadSettings*(rawJson: string, errors: var seq[string]): bool
proc loadSettingsFromFile*(filename: 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 fetchObj*(name: string): PObjectRecord
proc fetchBullet(name: string): PBulletRecord proc fetchBullet(name: string): PBulletRecord
proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings proc importLevel(data: JsonNode; errors: var seq[string]): PLevelSettings
proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord proc importVeh(data: JsonNode; errors: var seq[string]): PVehicleRecord
proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord proc importObject(data: JsonNode; errors: var seq[string]): PObjectRecord
proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord proc importItem(data: JsonNode; errors: var seq[string]): PItemRecord
proc importPhys(data: PJsonNode): TPhysicsRecord proc importPhys(data: JsonNode): TPhysicsRecord
proc importAnim(data: PJsonNode; errors: var seq[string]): PAnimationRecord proc importAnim(data: JsonNode; errors: var seq[string]): PAnimationRecord
proc importHandling(data: PJsonNode): THandlingRecord proc importHandling(data: JsonNode): THandlingRecord
proc importBullet(data: PJsonNode; errors: var seq[string]): PBulletRecord proc importBullet(data: JsonNode; errors: var seq[string]): PBulletRecord
proc importSoul(data: PJsonNode): TSoulRecord proc importSoul(data: JsonNode): TSoulRecord
proc importExplosion(data: PJsonNode; errors: var seq[string]): TExplosionRecord proc importExplosion(data: JsonNode; errors: var seq[string]): TExplosionRecord
proc importSound*(data: PJsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord proc importSound*(data: JsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord
## this is the only pipe between lobby and main.nim ## this is the only pipe between lobby and main.nim
proc getActiveState*(): TGameState = proc getActiveState*(): TGameState =
@ -203,7 +203,7 @@ iterator playableVehicles*(): PVehicleRecord =
if v.playable: if v.playable:
yield v yield v
template allAssets*(body: stmt) {.dirty.}= template allAssets*(body: untyped) {.dirty.}=
block: block:
var assetType = FGraphics var assetType = FGraphics
for file, asset in pairs(SpriteSheets): for file, asset in pairs(SpriteSheets):
@ -212,7 +212,7 @@ template allAssets*(body: stmt) {.dirty.}=
for file, asset in pairs(SoundCache): for file, asset in pairs(SoundCache):
body 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 = proc procName*(filename: string; errors: var seq[string]): resulttype =
if hasKey(cacheName, filename): if hasKey(cacheName, filename):
return cacheName[filename] return cacheName[filename]
@ -220,7 +220,7 @@ template cacheImpl(procName, cacheName, resultType: expr; body: stmt) {.dirty, i
body body
cacheName[filename] = result cacheName[filename] = result
template checkFile(path: expr): stmt {.dirty, immediate.} = template checkFile(path: untyped) {.dirty.} =
if not existsFile(path): if not existsFile(path):
errors.add("File missing: "& path) errors.add("File missing: "& path)
@ -243,7 +243,7 @@ proc expandPath*(assetType: TAssetType; fileName: string): string =
case assetType case assetType
of FGraphics: result.add "gfx/" of FGraphics: result.add "gfx/"
of FSound: result.add "sfx/" of FSound: result.add "sfx/"
else: nil else: discard
result.add fileName result.add fileName
proc expandPath*(fc: ScFileChallenge): string {.inline.} = proc expandPath*(fc: ScFileChallenge): string {.inline.} =
result = expandPath(fc.assetType, fc.file) result = expandPath(fc.assetType, fc.file)
@ -280,10 +280,10 @@ else:
if not s.soundBuf.isNil: if not s.soundBuf.isNil:
result = true result = true
template addError(e: expr): stmt {.immediate.} = template addError(e: untyped) =
errors.add(e) errors.add(e)
result = false result = false
proc validateSettings*(settings: PJsonNode, errors: var seq[string]): bool = proc validateSettings*(settings: JsonNode, errors: var seq[string]): bool =
result = true result = true
if settings.kind != JObject: if settings.kind != JObject:
addError("Settings root must be an object") 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) result = loadSettings(readFile(filename), errors)
proc loadSettings*(rawJson: string, errors: var seq[string]): bool = proc loadSettings*(rawJson: string, errors: var seq[string]): bool =
var settings: PJsonNode var settings: JsonNode
try: try:
settings = parseJson(rawJson) settings = parseJson(rawJson)
except EJsonParsingError: except JsonParsingError:
errors.add("JSON parsing error: "& getCurrentExceptionMsg()) errors.add("JSON parsing error: "& getCurrentExceptionMsg())
return return
except: except:
@ -407,21 +407,21 @@ proc fetchObj*(name: string): PObjectRecord =
proc fetchBullet(name: string): PBulletRecord = proc fetchBullet(name: string): PBulletRecord =
return cfg.bullets[nameToBulletID[name]] 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): if not node.hasKey(field):
return return
if node[field].kind == JFloat: if node[field].kind == JFloat:
target = node[field].fnum target = node[field].fnum
elif node[field].kind == JInt: elif node[field].kind == JInt:
target = node[field].num.float 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): if not node.hasKey(field):
return return
if node[field].kind == JInt: if node[field].kind == JInt:
target = node[field].num.int target = node[field].num.int
elif node[field].kind == JFloat: elif node[field].kind == JFloat:
target = node[field].fnum.int 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): if not node.hasKey(field):
return return
case node[field].kind case node[field].kind
@ -431,19 +431,19 @@ proc getField(node: PJsonNode; field: string; target: var bool) =
target = (node[field].num != 0) target = (node[field].num != 0)
of JFloat: of JFloat:
target = (node[field].fnum != 0.0) 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): if not hasKey(node, key):
return return
proc importTrail(data: PJsonNode; errors: var seq[string]): TTrailRecord = proc importTrail(data: JsonNode; errors: var seq[string]): TTrailRecord =
checkKey(data, "trail") checkKey(data, "trail")
result.anim = importAnim(data["trail"], errors) result.anim = importAnim(data["trail"], errors)
result.timer = 1000.0 result.timer = 1000.0
getField(data["trail"], "timer", result.timer) getField(data["trail"], "timer", result.timer)
result.timer /= 1000.0 result.timer /= 1000.0
proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings = proc importLevel(data: JsonNode; errors: var seq[string]): PLevelSettings =
new(result) new(result)
result.size = vec2i(5000, 5000) result.size = vec2i(5000, 5000)
result.starfield = @[] result.starfield = @[]
@ -456,7 +456,7 @@ proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings =
if level.hasKey("starfield"): if level.hasKey("starfield"):
for star in level["starfield"].items: for star in level["starfield"].items:
result.starfield.add(newSprite(star.str, errors)) result.starfield.add(newSprite(star.str, errors))
proc importPhys(data: PJsonNode): TPhysicsRecord = proc importPhys(data: JsonNode): TPhysicsRecord =
result.radius = 20.0 result.radius = 20.0
result.mass = 10.0 result.mass = 10.0
@ -466,7 +466,7 @@ proc importPhys(data: PJsonNode): TPhysicsRecord =
phys.getField("mass", result.mass) phys.getField("mass", result.mass)
when not defined(NoChipmunk): when not defined(NoChipmunk):
result.moment = momentForCircle(result.mass, 0.0, result.radius, VectorZero) * MomentMult 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.thrust = 45.0
result.topSpeed = 100.0 #unused result.topSpeed = 100.0 #unused
result.reverse = 30.0 result.reverse = 30.0
@ -483,7 +483,7 @@ proc importHandling(data: PJsonNode): THandlingRecord =
hand.getField("reverse", result.reverse) hand.getField("reverse", result.reverse)
hand.getField("strafe", result.strafe) hand.getField("strafe", result.strafe)
hand.getField("rotation", result.rotation) 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) new(result)
result.angle = 0.0 result.angle = 0.0
result.delay = 1000.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.angle = radians(result.angle) ## comes in as degrees
result.delay /= 1000 ## delay comes in as milliseconds result.delay /= 1000 ## delay comes in as milliseconds
proc importSoul(data: PJsonNode): TSoulRecord = proc importSoul(data: JsonNode): TSoulRecord =
result.energy = 10000 result.energy = 10000
result.health = 1 result.health = 1
checkKey(data, "soul") checkKey(data, "soul")
let soul = data["soul"] let soul = data["soul"]
soul.getField("energy", result.energy) soul.getField("energy", result.energy)
soul.getField("health", result.health) 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") checkKey(data, "explode")
let expl = data["explode"] let expl = data["explode"]
result.anim = importAnim(expl, errors) result.anim = importAnim(expl, errors)
result.sound = importSound(expl, errors, "sound") 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: if data.kind == JObject:
checkKey(data, fieldName) checkKey(data, fieldName)
result = newSound(data[fieldName].str, errors) result = newSound(data[fieldName].str, errors)
elif data.kind == JString: elif data.kind == JString:
result = newSound(data.str, errors) 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) new(result)
result.playable = false result.playable = false
if data.kind != JArray or data.len != 2 or 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) vehdata.getField("playable", result.playable)
if result.anim.spriteSheet.isNil and result.playable: if result.anim.spriteSheet.isNil and result.playable:
result.playable = false result.playable = false
proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord = proc importObject(data: JsonNode; errors: var seq[string]): PObjectRecord =
new(result) new(result)
if data.kind != JArray or data.len != 2: if data.kind != JArray or data.len != 2:
result.name = "(broken)" result.name = "(broken)"
@ -546,7 +546,7 @@ proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord =
result.name = data[0].str result.name = data[0].str
result.anim = importAnim(data[1], errors) result.anim = importAnim(data[1], errors)
result.physics = importPhys(data[1]) 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) new(result)
if data.kind != JArray or data.len != 3: if data.kind != JArray or data.len != 3:
result.name = "(broken)" result.name = "(broken)"
@ -562,7 +562,7 @@ proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
result.useSound = importSound(data[2], errors, "useSound") result.useSound = importSound(data[2], errors, "useSound")
case data[1].str.toLower case data[1].str.toLowerAscii
of "projectile": of "projectile":
result.kind = Projectile result.kind = Projectile
if data[2]["bullet"].kind == JString: if data[2]["bullet"].kind == JString:
@ -576,15 +576,15 @@ proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
of "ammo": of "ammo":
result.kind = Ammo result.kind = Ammo
of "utility": of "utility":
nil discard
else: else:
errors.add "Invalid item type \""&data[1].str&"\" for item "&result.name 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) new(result)
result.id = -1 result.id = -1
var bdata: PJsonNode var bdata: JsonNode
if data.kind == JArray: if data.kind == JArray:
result.name = data[0].str result.name = data[0].str
bdata = data[1] bdata = data[1]

View file

@ -4,14 +4,14 @@ defPacketImports()
type type
PacketID* = char 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 let `H pktName`* {.inject.} = id
defPacket(`Sc pktName`, s2c) defPacket(`Sc pktName`, s2c)
defPacket(`Cs pktName`, c2s) defPacket(`Cs pktName`, c2s)
forwardPacketT(uint8, int8) forwardPacketT(uint8, int8)
forwardPacketT(uint16, int16) forwardPacketT(uint16, int16)
forwardPacketT(TPort, int16) forwardPacketT(Port, int16)
idPacket(Login, 'a', idPacket(Login, 'a',
tuple[id: int32; alias: string; sessionKey: string], tuple[id: int32; alias: string; sessionKey: string],
@ -22,7 +22,7 @@ defPacket(CsZoneJoinReq, tuple[session: ScLogin])
defPacket(ScZoneRecord, tuple[ defPacket(ScZoneRecord, tuple[
name: string = "", desc: string = "", name: string = "", desc: string = "",
ip: string = "", port: TPort = 0.Tport]) ip: string = "", port: Port = 0.Port])
idPacket(ZoneList, 'z', idPacket(ZoneList, 'z',
tuple[network: string = "", zones: seq[ScZoneRecord]], tuple[network: string = "", zones: seq[ScZoneRecord]],
tuple[time: string]) tuple[time: string])

View file

@ -67,7 +67,7 @@ else:
for kind, key, val in getOpt(): for kind, key, val in getOpt():
case kind case kind
of cmdLongOption, cmdShortOption: of cmdLongOption, cmdShortOption:
case key.tolower case key.tolowerAscii
of "tasks", "t": of "tasks", "t":
printTaskList = true printTaskList = true
else: else:

View file

@ -88,7 +88,7 @@ task "download", "download game assets":
if existsFile(path): if existsFile(path):
echo "The file already exists\n", echo "The file already exists\n",
"[R]emove [M]ove [Q]uit [S]kip Source: ", GameAssets "[R]emove [M]ove [Q]uit [S]kip Source: ", GameAssets
case stdin.readLine.toLower case stdin.readLine.toLowerAscii
of "r": of "r":
removeFile path removeFile path
of "m": 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", echo "Download binary libs? Only libs for linux are available currently, enjoy the irony.\n",
"[Y]es [N]o Source: ", BinLibs "[Y]es [N]o Source: ", BinLibs
case stdin.readline.toLower case stdin.readline.toLowerAscii
of "y", "yes": of "y", "yes":
discard ## o_O discard ## o_O
else: else:

View file

@ -46,7 +46,7 @@ type
type1 = typedesc type1 = typedesc
type2 = 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(int, int, TFoo, TFOO)
accept typePairs(TBAR, TBar, TBAR, TBAR) accept typePairs(TBAR, TBar, TBAR, TBAR)
@ -55,7 +55,7 @@ accept typePairs(int, int, string, string)
reject typePairs(TBAR, TBar, TBar, TFoo) reject typePairs(TBAR, TBar, TBar, TFoo)
reject typePairs(string, int, TBAR, TBAR) 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(int, int, TFoo, TFOO)
accept typePairs2(TBAR, TBar, TBAR, TBAR) accept typePairs2(TBAR, TBar, TBAR, TBAR)
@ -71,12 +71,12 @@ proc dontBind(a: typedesc, b: typedesc) =
accept dontBind(int, float) accept dontBind(int, float)
accept dontBind(TFoo, TFoo) accept dontBind(TFoo, TFoo)
proc dontBind2(a, b: typedesc) = nil proc dontBind2(a, b: typedesc) = discard
accept dontBind2(int, float) accept dontBind2(int, float)
accept dontBind2(TBar, int) 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, string, 10, 20, "test", "nest")
accept bindArg(int, int, 10, 20, 30, 40) accept bindArg(int, int, 10, 20, 30, 40)

View file

@ -6,7 +6,7 @@ discard """
import strutils import strutils
var x = "hello world!".toLower.toUpper var x = "hello world!".toLowerAscii.toUpperAscii
x.echo() x.echo()
#OUT HELLO WORLD! #OUT HELLO WORLD!

View file

@ -8,7 +8,7 @@ import strutils
proc foo(s: static[string]): string = proc foo(s: static[string]): string =
static: echo s static: echo s
const R = s.toUpper const R = s.toUpperAscii
return R return R
echo foo("test 1") echo foo("test 1")

23
tests/misc/tsemfold.nim Normal file
View file

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

View file

@ -3,6 +3,7 @@ discard """
""" """
# bug #6682 # bug #6682
{.experimental: "notnil".}
type type
Fields = enum Fields = enum

View file

@ -2,7 +2,7 @@ discard """
line: 22 line: 22
errormsg: "type mismatch" errormsg: "type mismatch"
""" """
{.experimental: "notnil".}
type type
PObj = ref TObj not nil PObj = ref TObj not nil
TObj = object TObj = object
@ -15,8 +15,8 @@ type
proc p(x: string not nil): int = proc p(x: string not nil): int =
result = 45 result = 45
proc q(x: MyString) = nil proc q(x: MyString) = discard
proc q2(x: string) = nil proc q2(x: string) = discard
q2(nil) q2(nil)
q(nil) q(nil)

View file

@ -4,7 +4,7 @@ discard """
""" """
import strutils import strutils
{.experimental: "notnil".}
type type
TObj = object TObj = object
@ -24,7 +24,7 @@ proc p2() =
p2() p2()
proc q(x: pointer not nil) = proc q(x: pointer not nil) =
nil discard
proc p() = proc p() =
var x: pointer var x: pointer

View file

@ -4,14 +4,14 @@ discard """
""" """
import strutils import strutils
{.experimental: "notnil".}
type type
TObj = object TObj = object
x, y: int x, y: int
proc q(x: pointer not nil) = proc q(x: pointer not nil) =
nil discard
proc p() = proc p() =
var x: pointer var x: pointer

View file

@ -5,7 +5,7 @@ discard """
# bug #584 # bug #584
# Testprogram for 'not nil' check # Testprogram for 'not nil' check
{.experimental: "notnil".}
const testWithResult = true const testWithResult = true
type type

View file

@ -2,6 +2,8 @@ discard ""
type type
TObj = ref object TObj = ref object
{.experimental: "notnil".}
proc check(a: TObj not nil) = proc check(a: TObj not nil) =
echo repr(a) echo repr(a)

View file

@ -3,6 +3,7 @@ discard """
""" """
# bug #2216 # bug #2216
{.experimental: "notnil".}
type type
A[T] = ref object A[T] = ref object

View file

@ -2,7 +2,7 @@ discard """
errormsg: "fields not initialized: bar" errormsg: "fields not initialized: bar"
line: "13" line: "13"
""" """
{.experimental: "notnil".}
# bug #2355 # bug #2355
type type
Foo = object Foo = object

View file

@ -6,7 +6,7 @@ discard """
import strutils import strutils
{.warning[ProveField]: on.} {.warning[ProveField]: on.}
{.experimental: "notnil".}
type type
TNodeKind = enum TNodeKind = enum
nkBinary, nkTernary, nkStr nkBinary, nkTernary, nkStr

View file

@ -5,7 +5,7 @@ discard """
type type
PTest = ref object PTest = ref object
proc test(x: PTest, y: int) = nil proc test(x: PTest, y: int) = discard
var buf: PTest var buf: PTest
buf.test() buf.test()

View file

@ -4,6 +4,6 @@ discard """
errormsg: "redefinition of \'foo\'" errormsg: "redefinition of \'foo\'"
""" """
proc foo(a: int, b: string) = nil proc foo(a: int, b: string) = discard
proc foo(a: int, b: string) = nil proc foo(a: int, b: string) = discard

View file

@ -7,7 +7,7 @@ type
TRange = range[0..40] TRange = range[0..40]
proc p(r: TRange) = proc p(r: TRange) =
nil discard
var var
r: TRange r: TRange

File diff suppressed because it is too large Load diff

View file

@ -30,7 +30,6 @@ else:
result = newString(size) result = newString(size)
let res = WideCharToMultiByte(CP_UTF8, 0'i32, cast[LPWCSTR](addr(wc[0])), wclen, let res = WideCharToMultiByte(CP_UTF8, 0'i32, cast[LPWCSTR](addr(wc[0])), wclen,
cstring(result), size, cstring(nil), LPBOOL(nil)) cstring(result), size, cstring(nil), LPBOOL(nil))
result[size] = chr(0)
doAssert size == res doAssert size == res
proc testCP(wc: WideCString, lo, hi: int) = proc testCP(wc: WideCString, lo, hi: int) =

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