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
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
@@ -11,9 +11,12 @@
to deal with!
- Indexing into a ``cstring`` for the JS target is now mapped
to ``charCodeAt``.
- Assignments that would "slice" an object into its supertype are not prevented
- Assignments that would "slice" an object into its supertype are now prevented
at runtime. Use ``ref object`` with inheritance rather than ``object`` with
inheritance to prevent this issue.
- The ``not nil`` type annotation now has to be enabled explicitly
via ``{.experimental: "notnil"}`` as we are still not pleased with how this
feature works with Nim's containers.
#### Breaking changes in the standard library
@@ -81,6 +84,14 @@
Imported exceptions can be raised and caught just like Nim exceptions.
More details in language manual.
- ``nil`` for strings/seqs is finally gone. Instead the default value for
these is ``"" / @[]``.
- Accessing the binary zero terminator in Nim's native strings
is now invalid. Internally a Nim string still has the trailing zero for
zero-copy interoperability with ``cstring``. Compile your code with the
next switch ``--laxStrings:on`` if you need a transition period.
### Tool changes
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,7 +13,7 @@
import
strutils, options, ast, astalgo, trees, treetab, nimsets, times,
nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
commands, magicsys, saturate
commands, magicsys
proc getConstExpr*(m: PSym, n: PNode): PNode
# evaluates the constant expression or returns nil if it is no constant
@@ -24,6 +24,63 @@ proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode
proc newStrNodeT*(strVal: string, n: PNode): PNode
proc checkInRange(n: PNode, res: BiggestInt): bool =
if res in firstOrd(n.typ)..lastOrd(n.typ):
result = true
proc foldAdd(a, b: BiggestInt, n: PNode): PNode =
let res = a +% b
if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and
checkInRange(n, res):
result = newIntNodeT(res, n)
proc foldSub*(a, b: BiggestInt, n: PNode): PNode =
let res = a -% b
if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and
checkInRange(n, res):
result = newIntNodeT(res, n)
proc foldAbs*(a: BiggestInt, n: PNode): PNode =
if a != firstOrd(n.typ):
result = newIntNodeT(a, n)
proc foldMod*(a, b: BiggestInt, n: PNode): PNode =
if b != 0'i64:
result = newIntNodeT(a mod b, n)
proc foldModU*(a, b: BiggestInt, n: PNode): PNode =
if b != 0'i64:
result = newIntNodeT(a %% b, n)
proc foldDiv*(a, b: BiggestInt, n: PNode): PNode =
if b != 0'i64 and (a != firstOrd(n.typ) or b != -1'i64):
result = newIntNodeT(a div b, n)
proc foldDivU*(a, b: BiggestInt, n: PNode): PNode =
if b != 0'i64:
result = newIntNodeT(a /% b, n)
proc foldMul*(a, b: BiggestInt, n: PNode): PNode =
let res = a *% b
let floatProd = toBiggestFloat(a) * toBiggestFloat(b)
let resAsFloat = toBiggestFloat(res)
# Fast path for normal case: small multiplicands, and no info
# is lost in either method.
if resAsFloat == floatProd and checkInRange(n, res):
return newIntNodeT(res, n)
# Somebody somewhere lost info. Close enough, or way off? Note
# that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
# The difference either is or isn't significant compared to the
# true value (of which floatProd is a good approximation).
# abs(diff)/abs(prod) <= 1/32 iff
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and
checkInRange(n, res):
return newIntNodeT(res, n)
# implementation
proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
@@ -172,23 +229,22 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
of mToFloat, mToBiggestFloat:
result = newFloatNodeT(toFloat(int(getInt(a))), n)
# XXX: Hides overflow/underflow
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
of mAbsI:
if getInt(a) >= 0: result = a
else: result = newIntNodeT(- getInt(a), n)
of mAbsI: result = foldAbs(getInt(a), n)
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
# byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n)
of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n)
of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n)
of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n)
of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n)
of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n)
of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n)
of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n)
of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n)
of mUnaryLt: result = foldSub(getOrdValue(a), 1, n)
of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n)
of mPred: result = foldSub(getOrdValue(a), getInt(b), n)
of mAddI: result = foldAdd(getInt(a), getInt(b), n)
of mSubI: result = foldSub(getInt(a), getInt(b), n)
of mMulI: result = foldMul(getInt(a), getInt(b), n)
of mMinI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
else: result = newIntNodeT(getInt(a), n)
@@ -211,14 +267,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shr`(getInt(a), getInt(b)), n)
else: internalError(n.info, "constant folding for shr")
of mDivI:
let y = getInt(b)
if y != 0:
result = newIntNodeT(`|div|`(getInt(a), y), n)
of mModI:
let y = getInt(b)
if y != 0:
result = newIntNodeT(`|mod|`(getInt(a), y), n)
of mDivI: result = foldDiv(getInt(a), getInt(b), n)
of mModI: result = foldMod(getInt(a), getInt(b), n)
of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
@@ -258,14 +308,8 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n)
of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n)
of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n)
of mModU:
let y = getInt(b)
if y != 0:
result = newIntNodeT(`%%`(getInt(a), y), n)
of mDivU:
let y = getInt(b)
if y != 0:
result = newIntNodeT(`/%`(getInt(a), y), n)
of mModU: result = foldModU(getInt(a), getInt(b), n)
of mDivU: result = foldDivU(getInt(a), getInt(b), n)
of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n)
of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n)
of mLtSet:

View File

@@ -71,37 +71,12 @@ proc toCover(t: PType): BiggestInt =
else:
result = lengthOrd(skipTypes(t, abstractVar-{tyTypeDesc}))
when false:
proc performProcvarCheck(c: PContext, info: TLineInfo, s: PSym) =
## Checks that the given symbol is a proper procedure variable, meaning
## that it
var smoduleId = getModule(s).id
if sfProcvar notin s.flags and s.typ.callConv == ccDefault and
smoduleId != c.module.id:
block outer:
for module in c.friendModules:
if smoduleId == module.id:
break outer
localError(info, errXCannotBePassedToProcVar, s.name.s)
template semProcvarCheck(c: PContext, n: PNode) =
when false:
var n = n.skipConv
if n.kind in nkSymChoices:
for x in n:
if x.sym.kind in {skProc, skMethod, skConverter, skIterator}:
performProcvarCheck(c, n.info, x.sym)
elif n.kind == nkSym and n.sym.kind in {skProc, skMethod, skConverter,
skIterator}:
performProcvarCheck(c, n.info, n.sym)
proc semProc(c: PContext, n: PNode): PNode
proc semExprBranch(c: PContext, n: PNode): PNode =
result = semExpr(c, n)
if result.typ != nil:
# XXX tyGenericInst here?
semProcvarCheck(c, result)
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
proc semExprBranchScope(c: PContext, n: PNode): PNode =
@@ -132,30 +107,23 @@ proc fixNilType(n: PNode) =
proc discardCheck(c: PContext, result: PNode) =
if c.matchedConcept != nil: return
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
if result.kind == nkNilLit:
result.typ = nil
message(result.info, warnNilStatement)
elif implicitlyDiscardable(result):
if implicitlyDiscardable(result):
var n = result
result.typ = nil
while n.kind in skipForDiscardable:
n = n.lastSon
n.typ = nil
elif result.typ.kind != tyError and gCmd != cmdInteractive:
if result.typ.kind == tyNil:
fixNilType(result)
message(result.info, warnNilStatement)
else:
var n = result
while n.kind in skipForDiscardable: n = n.lastSon
var s = "expression '" & $n & "' is of type '" &
result.typ.typeToString & "' and has to be discarded"
if result.info.line != n.info.line or
result.info.fileIndex != n.info.fileIndex:
s.add "; start of expression here: " & $result.info
if result.typ.kind == tyProc:
s.add "; for a function call use ()"
localError(n.info, s)
var n = result
while n.kind in skipForDiscardable: n = n.lastSon
var s = "expression '" & $n & "' is of type '" &
result.typ.typeToString & "' and has to be discarded"
if result.info.line != n.info.line or
result.info.fileIndex != n.info.fileIndex:
s.add "; start of expression here: " & $result.info
if result.typ.kind == tyProc:
s.add "; for a function call use ()"
localError(n.info, s)
proc semIf(c: PContext, n: PNode): PNode =
result = n
@@ -384,7 +352,7 @@ proc checkNilable(v: PSym) =
include semasgn
proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) =
let L = identDefs.len
let value = identDefs[L-1]
if result.kind == nkStmtList:
@@ -525,12 +493,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
localError(a.info, errXExpected, "tuple")
elif length-2 != sonsLen(tup):
localError(a.info, errWrongNumberOfVariables)
else:
b = newNodeI(nkVarTuple, a.info)
newSons(b, length)
b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
b.sons[length-1] = def
addToVarSection(c, result, n, b)
b = newNodeI(nkVarTuple, a.info)
newSons(b, length)
b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator
b.sons[length-1] = def
addToVarSection(c, result, n, b)
elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and
a.kind == nkIdentDefs and a.len > 3:
message(a.info, warnEachIdentIsTuple)
@@ -572,7 +539,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addToVarSection(c, result, n, b)
else:
if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
setVarType(v, tup.sons[j])
# bug #7663, for 'nim check' this can be a non-tuple:
if tup.kind == tyTuple: setVarType(v, tup.sons[j])
else: v.typ = tup
b.sons[j] = newSymNode(v)
checkNilable(v)
if sfCompileTime in v.flags: hasCompileTime = true
@@ -1588,9 +1557,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
if s.name.s[0] in {'.', '('}:
if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}:
message(n.info, warnDeprecated, "overloaded '.' and '()' operators are now .experimental; " & s.name.s)
localError(n.info, "the overloaded " & s.name.s &
" operator has to be enabled with {.experimental: \"dotOperators\".}")
elif s.name.s == "()" and callOperator notin c.features:
message(n.info, warnDeprecated, "overloaded '()' operators are now .experimental; " & s.name.s)
localError(n.info, "the overloaded " & s.name.s &
" operator has to be enabled with {.experimental: \"callOperator\".}")
if n.sons[bodyPos].kind != nkEmpty:
# for DLL generation it is annoying to check for sfImportc!
@@ -1682,11 +1653,6 @@ proc semIterator(c: PContext, n: PNode): PNode =
incl(s.typ.flags, tfCapturesEnv)
else:
s.typ.callConv = ccInline
when false:
if s.typ.callConv != ccInline:
s.typ.callConv = ccClosure
# and they always at least use the 'env' for the state field:
incl(s.typ.flags, tfCapturesEnv)
if n.sons[bodyPos].kind == nkEmpty and s.magic == mNone:
localError(n.info, errImplOfXexpected, s.name.s)
@@ -1792,14 +1758,6 @@ proc semStaticStmt(c: PContext, n: PNode): PNode =
evalStaticStmt(c.module, c.cache, a, c.p.owner)
result = newNodeI(nkDiscardStmt, n.info, 1)
result.sons[0] = emptyNode
when false:
result = evalStaticStmt(c.module, a, c.p.owner)
if result.isNil:
LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
result = emptyNode
elif result.kind == nkEmpty:
result = newNodeI(nkDiscardStmt, n.info, 1)
result.sons[0] = emptyNode
proc usesResult(n: PNode): bool =
# nkStmtList(expr) properly propagates the void context,
@@ -1841,80 +1799,47 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
# nkNilLit, nkEmpty}:
# dec last
for i in countup(0, length - 1):
let k = n.sons[i].kind
case k
of nkFinally, nkExceptBranch:
# stand-alone finally and except blocks are
# transformed into regular try blocks:
#
# var f = fopen("somefile") | var f = fopen("somefile")
# finally: fclose(f) | try:
# ... | ...
# | finally:
# | fclose(f)
var deferPart: PNode
if k == nkDefer:
deferPart = newNodeI(nkFinally, n.sons[i].info)
deferPart.add n.sons[i].sons[0]
elif k == nkFinally:
message(n.info, warnDeprecated,
"use 'defer'; standalone 'finally'")
deferPart = n.sons[i]
else:
message(n.info, warnDeprecated,
"use an explicit 'try'; standalone 'except'")
deferPart = n.sons[i]
var tryStmt = newNodeI(nkTryStmt, n.sons[i].info)
var body = newNodeI(nkStmtList, n.sons[i].info)
if i < n.sonsLen - 1:
body.sons = n.sons[(i+1)..n.len-1]
tryStmt.addSon(body)
tryStmt.addSon(deferPart)
n.sons[i] = semTry(c, tryStmt)
n.sons.setLen(i+1)
n.typ = n.sons[i].typ
return
else:
var expr = semExpr(c, n.sons[i], flags)
n.sons[i] = expr
if c.matchedConcept != nil and expr.typ != nil and
(nfFromTemplate notin n.flags or i != last):
case expr.typ.kind
of tyBool:
if expr.kind == nkInfix and
expr[0].kind == nkSym and
expr[0].sym.name.s == "==":
if expr[1].typ.isUnresolvedStatic:
inferConceptStaticParam(c, expr[1], expr[2])
continue
elif expr[2].typ.isUnresolvedStatic:
inferConceptStaticParam(c, expr[2], expr[1])
continue
var expr = semExpr(c, n.sons[i], flags)
n.sons[i] = expr
if c.matchedConcept != nil and expr.typ != nil and
(nfFromTemplate notin n.flags or i != last):
case expr.typ.kind
of tyBool:
if expr.kind == nkInfix and
expr[0].kind == nkSym and
expr[0].sym.name.s == "==":
if expr[1].typ.isUnresolvedStatic:
inferConceptStaticParam(c, expr[1], expr[2])
continue
elif expr[2].typ.isUnresolvedStatic:
inferConceptStaticParam(c, expr[2], expr[1])
continue
let verdict = semConstExpr(c, n[i])
if verdict.intVal == 0:
localError(result.info, "concept predicate failed")
of tyUnknown: continue
else: discard
if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
voidContext = true
n.typ = enforceVoidContext
if i == last and (length == 1 or efWantValue in flags):
n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
elif i != last or voidContext:
discardCheck(c, n.sons[i])
else:
n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
if n.sons[i].kind in LastBlockStmts or
n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and sfNoReturn in n.sons[i][0].sym.flags:
for j in countup(i + 1, length - 1):
case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
nkBlockStmt, nkState: discard
else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
let verdict = semConstExpr(c, n[i])
if verdict.intVal == 0:
localError(result.info, "concept predicate failed")
of tyUnknown: continue
else: discard
if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
voidContext = true
n.typ = enforceVoidContext
if i == last and (length == 1 or efWantValue in flags):
n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
elif i != last or voidContext:
discardCheck(c, n.sons[i])
else:
n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr
if n.sons[i].kind in LastBlockStmts or
n.sons[i].kind in nkCallKinds and n.sons[i][0].kind == nkSym and
sfNoReturn in n.sons[i][0].sym.flags:
for j in countup(i + 1, length - 1):
case n.sons[j].kind
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkBlockExpr,
nkBlockStmt, nkState: discard
else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
else: discard
if result.len == 1 and
# concept bodies should be preserved as a stmt list:
@@ -1930,15 +1855,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
not (result.comment[0] == '#' and result.comment[1] == '#'):
# it is an old-style comment statement: we replace it with 'discard ""':
prettybase.replaceComment(result.info)
when false:
# a statement list (s; e) has the type 'e':
if result.kind == nkStmtList and result.len > 0:
var lastStmt = lastSon(result)
if lastStmt.kind != nkNilLit and not implicitlyDiscardable(lastStmt):
result.typ = lastStmt.typ
#localError(lastStmt.info, errGenerated,
# "Last expression must be explicitly returned if it " &
# "is discardable or discarded")
proc semStmt(c: PContext, n: PNode): PNode =
# now: simply an alias:

View File

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

View File

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

View File

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

View File

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

View File

@@ -208,7 +208,12 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
result.add mapTypeToAst(t.sons[0], info)
else:
result = mapTypeToBracket("ref", mRef, t, info)
of tyVar: result = mapTypeToBracket("var", mVar, t, info)
of tyVar:
if inst:
result = newNodeX(nkVarTy)
result.add mapTypeToAst(t.sons[0], info)
else:
result = mapTypeToBracket("var", mVar, t, info)
of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info)
of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info)
of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)

View File

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

View File

@@ -66,6 +66,8 @@ Advanced options:
--excessiveStackTrace:on|off
stack traces use full file paths
--oldNewlines:on|off turn on|off the old behaviour of "\n"
--laxStrings:on|off when turned on, accessing the zero terminator in
strings is allowed; only for backwards compatibility
--skipCfg do not read the general configuration file
--skipUserCfg do not read the user's configuration file
--skipParentCfg do not read the parent dirs' configuration files

View File

@@ -1471,7 +1471,7 @@ mysterious crashes.
**Note**: The example only works because the memory is initialized to zero
(``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to
``nil`` which the string assignment can handle. One needs to know low level
binary zero which the string assignment can handle. One needs to know low level
details like this when mixing garbage collected data with unmanaged memory.
.. XXX finalizers for traced objects
@@ -2512,8 +2512,8 @@ char '\\0'
bool false
ref or pointer type nil
procedural type nil
sequence nil (*not* ``@[]``)
string nil (*not* "")
sequence ``@[]``
string ``""``
tuple[x: A, y: B, ...] (default(A), default(B), ...)
(analogous for objects)
array[0..., T] [default(T), ...]
@@ -4270,7 +4270,7 @@ therefore very useful for type specialization within generic code:
Table[Key, Value] = object
keys: seq[Key]
values: seq[Value]
when not (Key is string): # nil value for strings used for optimization
when not (Key is string): # empty value for strings used for optimization
deletedKeys: seq[bool]
@@ -7434,8 +7434,8 @@ code generation directly, but their presence can be detected by macros.
Custom pragmas are defined using templates annotated with pragma ``pragma``:
.. code-block:: nim
template dbTable(name: string, table_space: string = nil) {.pragma.}
template dbKey(name: string = nil, primary_key: bool = false) {.pragma.}
template dbTable(name: string, table_space: string = "") {.pragma.}
template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
template dbForeignKey(t: typedesc) {.pragma.}
template dbIgnore {.pragma.}

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
length field. A string's length can be retrieved with the builtin ``len``
procedure; the length never counts the terminating zero. Accessing the
terminating zero is not an error and often leads to simpler code:
.. code-block:: nim
if s[i] == 'a' and s[i+1] == 'b':
# no need to check whether ``i < len(s)``!
...
terminating zero is an error, it only exists so that a Nim string can be converted
to a ``cstring`` without doing a copy.
The assignment operator for strings copies the string. You can use the ``&``
operator to concatenate strings and ``add`` to append to a string.
@@ -960,11 +956,7 @@ enforced. For example, when reading strings from binary files, they are merely
a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of
``s``, not the i-th *unichar*.
String variables are initialized with a special value, called ``nil``. However,
most string operations cannot deal with ``nil`` (leading to an exception being
raised) for performance reasons. It is best to use empty strings ``""``
rather than ``nil`` as the *empty* value. But ``""`` often creates a string
object on the heap, so there is a trade-off to be made here.
A string variable is initialized with the empty string ``""``.
Integers
@@ -1309,11 +1301,7 @@ Example:
x: seq[int] # a reference to a sequence of integers
x = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence allocated on the heap
Sequence variables are initialized with ``nil``. However, most sequence
operations cannot deal with ``nil`` (leading to an exception being
raised) for performance reasons. Thus one should use empty sequences ``@[]``
rather than ``nil`` as the *empty* value. But ``@[]`` creates a sequence
object on the heap, so there is a trade-off to be made here.
Sequence variables are initialized with ``@[]``.
The ``for`` statement can be used with one or two variables when used with a
sequence. When you use the one variable form, the variable will hold the value
@@ -1355,11 +1343,9 @@ type does not matter.
.. code-block:: nim
:test: "nim c $1"
var
fruits: seq[string] # reference to a sequence of strings that is initialized with 'nil'
fruits: seq[string] # reference to a sequence of strings that is initialized with '@[]'
capitals: array[3, string] # array of strings with a fixed size
fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits'
capitals = ["New York", "London", "Berlin"] # array 'capitals' allows assignment of only three elements
fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime
fruits.add("Mango")
@@ -1691,7 +1677,7 @@ rules apply:
write(stdout, x(3)) # no error: A.x is called
write(stdout, x("")) # no error: B.x is called
proc x*(a: int): string = nil
proc x*(a: int): string = discard
write(stdout, x(3)) # ambiguous: which `x` is to call?

View File

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

View File

@@ -10,7 +10,7 @@
from pcre import nil
import nre.private.util
import tables
from strutils import toLower, `%`
from strutils import `%`
from math import ceil
import options
from unicode import runeLenAt
@@ -326,15 +326,15 @@ proc `$`*(pattern: RegexMatch): string =
proc `==`*(a, b: Regex): bool =
if not a.isNil and not b.isNil:
return a.pattern == b.pattern and
a.pcreObj == b.pcreObj and
return a.pattern == b.pattern and
a.pcreObj == b.pcreObj and
a.pcreExtra == b.pcreExtra
else:
return system.`==`(a, b)
proc `==`*(a, b: RegexMatch): bool =
return a.pattern == b.pattern and
a.str == b.str
a.str == b.str
# }}}
# Creation & Destruction {{{
@@ -645,7 +645,6 @@ template replaceImpl(str: string, pattern: Regex,
let bounds = match.matchBounds
result.add(str.substr(lastIdx, bounds.a - 1))
let nextVal = replacement
assert(nextVal != nil)
result.add(nextVal)
lastIdx = bounds.b + 1

View File

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

View File

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

View File

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

View File

@@ -52,7 +52,7 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
if numLines > 0: inc(total, (numLines - 1) * newLine.len)
result = newString(total)
var
var
i = 0
r = 0
currLine = 0
@@ -76,7 +76,7 @@ template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
currLine = 0
if i < s.len-1:
let
let
a = ord(s[i])
b = ord(s[i+1])
result[r] = cb64[a shr 2]
@@ -130,11 +130,11 @@ proc decode*(s: string): string =
# total is an upper bound, as we will skip arbitrary whitespace:
result = newString(total)
var
var
i = 0
r = 0
while true:
while s[i] in Whitespace: inc(i)
while i < s.len and s[i] in Whitespace: inc(i)
if i < s.len-3:
let
a = s[i].decodeByte

View File

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

View File

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

View File

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

View File

@@ -36,7 +36,7 @@ when defined(windows):
while i < a.len and j < b.len:
if a[i] in {'-', '_'}: inc i
if b[j] in {'-', '_'}: inc j
if a[i].toLower != b[j].toLower: return false
if i < a.len and j < b.len and a[i].toLowerAscii != b[j].toLowerAscii: return false
inc i
inc j
result = i == a.len and j == b.len

View File

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

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 =
var i = 0
var current = ""
while line[start + i] notin {'\c', '\l', '\0'}:
while start+i < line.len and line[start + i] notin {'\c', '\l'}:
i += line.skipWhitespace(start + i)
i += line.parseUntil(current, {'\c', '\l', ','}, start + i)
list.add(current)
if line[start + i] == ',':
if start+i < line.len and line[start + i] == ',':
i.inc # Skip ,
current.setLen(0)

View File

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

View File

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

View File

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

View File

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

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.
assert(not socket.isClosed, "Cannot `sendTo` on a closed socket")
var aiList = getAddrInfo(address, port, af)
var aiList = getAddrInfo(address, port, af, socket.sockType, socket.protocol)
# try all possibilities:
var success = false
var it = aiList
@@ -1443,7 +1442,7 @@ proc sendTo*(socket: Socket, address: string, port: Port,
## this function will try each IP of that hostname.
##
## This is the high-level version of the above ``sendTo`` function.
result = socket.sendTo(address, port, cstring(data), data.len)
result = socket.sendTo(address, port, cstring(data), data.len, socket.domain )
proc isSsl*(socket: Socket): bool =

View File

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

View File

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

View File

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

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

View File

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

View File

@@ -278,7 +278,7 @@ template callFormatOption(res, arg, option) {.dirty.} =
macro `&`*(pattern: string): untyped =
## For a specification of the ``&`` macro, see the module level documentation.
if pattern.kind notin {nnkStrLit..nnkTripleStrLit}:
error "& only works with string literals", pattern
error "string formatting (fmt(), &) only works with string literals", pattern
let f = pattern.strVal
var i = 0
let res = genSym(nskVar, "fmtRes")

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

View File

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

View File

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

View File

@@ -1318,24 +1318,23 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}=
result = ""
var i = 0
var currentF = ""
while true:
while i < f.len:
case f[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',':
formatToken(dt, currentF, result)
currentF = ""
if f[i] == '\0': break
if f[i] == '\'':
inc(i) # Skip '
while f[i] != '\'' and f.len-1 > i:
while i < f.len-1 and f[i] != '\'':
result.add(f[i])
inc(i)
else: result.add(f[i])
else:
# Check if the letter being added matches previous accumulated buffer.
if currentF.len < 1 or currentF[high(currentF)] == f[i]:
if currentF.len == 0 or currentF[high(currentF)] == f[i]:
currentF.add(f[i])
else:
formatToken(dt, currentF, result)
@@ -1343,6 +1342,7 @@ proc format*(dt: DateTime, f: string): string {.tags: [].}=
currentF = ""
inc(i)
formatToken(dt, currentF, result)
proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} =
## Converts a `DateTime` object to a string representation.
@@ -1439,58 +1439,58 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
dt.month = month.Month
of "MMM":
case value[j..j+2].toLowerAscii():
of "jan": dt.month = mJan
of "feb": dt.month = mFeb
of "mar": dt.month = mMar
of "apr": dt.month = mApr
of "may": dt.month = mMay
of "jun": dt.month = mJun
of "jul": dt.month = mJul
of "aug": dt.month = mAug
of "sep": dt.month = mSep
of "oct": dt.month = mOct
of "nov": dt.month = mNov
of "dec": dt.month = mDec
of "jan": dt.month = mJan
of "feb": dt.month = mFeb
of "mar": dt.month = mMar
of "apr": dt.month = mApr
of "may": dt.month = mMay
of "jun": dt.month = mJun
of "jul": dt.month = mJul
of "aug": dt.month = mAug
of "sep": dt.month = mSep
of "oct": dt.month = mOct
of "nov": dt.month = mNov
of "dec": dt.month = mDec
else:
raise newException(ValueError,
"Couldn't parse month (MMM), got: " & value)
j += 3
of "MMMM":
if value.len >= j+7 and value[j..j+6].cmpIgnoreCase("january") == 0:
dt.month = mJan
dt.month = mJan
j += 7
elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("february") == 0:
dt.month = mFeb
dt.month = mFeb
j += 8
elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("march") == 0:
dt.month = mMar
dt.month = mMar
j += 5
elif value.len >= j+5 and value[j..j+4].cmpIgnoreCase("april") == 0:
dt.month = mApr
dt.month = mApr
j += 5
elif value.len >= j+3 and value[j..j+2].cmpIgnoreCase("may") == 0:
dt.month = mMay
dt.month = mMay
j += 3
elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("june") == 0:
dt.month = mJun
dt.month = mJun
j += 4
elif value.len >= j+4 and value[j..j+3].cmpIgnoreCase("july") == 0:
dt.month = mJul
dt.month = mJul
j += 4
elif value.len >= j+6 and value[j..j+5].cmpIgnoreCase("august") == 0:
dt.month = mAug
dt.month = mAug
j += 6
elif value.len >= j+9 and value[j..j+8].cmpIgnoreCase("september") == 0:
dt.month = mSep
dt.month = mSep
j += 9
elif value.len >= j+7 and value[j..j+6].cmpIgnoreCase("october") == 0:
dt.month = mOct
dt.month = mOct
j += 7
elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("november") == 0:
dt.month = mNov
dt.month = mNov
j += 8
elif value.len >= j+8 and value[j..j+7].cmpIgnoreCase("december") == 0:
dt.month = mDec
dt.month = mDec
j += 8
else:
raise newException(ValueError,
@@ -1521,44 +1521,47 @@ proc parseToken(dt: var DateTime; token, value: string; j: var int) =
j += 4
of "z":
dt.isDst = false
if value[j] == '+':
let ch = if j < value.len: value[j] else: '\0'
if ch == '+':
dt.utcOffset = 0 - parseInt($value[j+1]) * secondsInHour
elif value[j] == '-':
elif ch == '-':
dt.utcOffset = parseInt($value[j+1]) * secondsInHour
elif value[j] == 'Z':
elif ch == 'Z':
dt.utcOffset = 0
j += 1
return
else:
raise newException(ValueError,
"Couldn't parse timezone offset (z), got: " & value[j])
"Couldn't parse timezone offset (z), got: " & ch)
j += 2
of "zz":
dt.isDst = false
if value[j] == '+':
let ch = if j < value.len: value[j] else: '\0'
if ch == '+':
dt.utcOffset = 0 - value[j+1..j+2].parseInt() * secondsInHour
elif value[j] == '-':
elif ch == '-':
dt.utcOffset = value[j+1..j+2].parseInt() * secondsInHour
elif value[j] == 'Z':
elif ch == 'Z':
dt.utcOffset = 0
j += 1
return
else:
raise newException(ValueError,
"Couldn't parse timezone offset (zz), got: " & value[j])
"Couldn't parse timezone offset (zz), got: " & ch)
j += 3
of "zzz":
dt.isDst = false
var factor = 0
if value[j] == '+': factor = -1
elif value[j] == '-': factor = 1
elif value[j] == 'Z':
let ch = if j < value.len: value[j] else: '\0'
if ch == '+': factor = -1
elif ch == '-': factor = 1
elif ch == 'Z':
dt.utcOffset = 0
j += 1
return
else:
raise newException(ValueError,
"Couldn't parse timezone offset (zzz), got: " & value[j])
"Couldn't parse timezone offset (zzz), got: " & ch)
dt.utcOffset = factor * value[j+1..j+2].parseInt() * secondsInHour
j += 4
dt.utcOffset += factor * value[j..j+1].parseInt() * 60
@@ -1620,20 +1623,18 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime =
dt.nanosecond = 0
dt.isDst = true # using this is flag for checking whether a timezone has \
# been read (because DST is always false when a tz is parsed)
while true:
while i < layout.len:
case layout[i]
of ' ', '-', '/', ':', '\'', '\0', '(', ')', '[', ']', ',':
of ' ', '-', '/', ':', '\'', '(', ')', '[', ']', ',':
if token.len > 0:
parseToken(dt, token, value, j)
# Reset token
token = ""
# Break if at end of line
if layout[i] == '\0': break
# Skip separator and everything between single quotes
# These are literals in both the layout and the value string
if layout[i] == '\'':
inc(i)
while layout[i] != '\'' and layout.len-1 > i:
while i < layout.len-1 and layout[i] != '\'':
inc(i)
inc(j)
inc(i)
@@ -1642,13 +1643,15 @@ proc parse*(value, layout: string, zone: Timezone = local()): DateTime =
inc(j)
else:
# Check if the letter being added matches previous accumulated buffer.
if token.len < 1 or token[high(token)] == layout[i]:
if token.len == 0 or token[high(token)] == layout[i]:
token.add(layout[i])
inc(i)
else:
parseToken(dt, token, value, j)
token = ""
if i >= layout.len and token.len > 0:
parseToken(dt, token, value, j)
if dt.isDst:
# No timezone parsed - assume timezone is `zone`
result = initDateTime(zone.zoneInfoFromTz(dt.toAdjTime), zone)

View File

@@ -60,7 +60,7 @@ proc encodeUrl*(s: string): string =
else:
add(result, '%')
add(result, toHex(ord(s[i]), 2))
proc decodeUrl*(s: string): string =
## Decodes a value from its HTTP representation: This means that a ``'+'``
## is converted to a space, ``'%xx'`` (where ``xx`` denotes a hexadecimal
@@ -72,7 +72,7 @@ proc decodeUrl*(s: string): string =
of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
else: assert(false)
result = newString(s.len)
var i = 0
var j = 0
@@ -94,7 +94,7 @@ proc parseAuthority(authority: string, result: var Uri) =
var i = 0
var inPort = false
var inIPv6 = false
while true:
while i < authority.len:
case authority[i]
of '@':
swap result.password, result.port
@@ -111,7 +111,6 @@ proc parseAuthority(authority: string, result: var Uri) =
inIPv6 = true
of ']':
inIPv6 = false
of '\0': break
else:
if inPort:
result.port.add(authority[i])
@@ -128,11 +127,11 @@ proc parsePath(uri: string, i: var int, result: var Uri) =
parseAuthority(result.path, result)
result.path.setLen(0)
if uri[i] == '?':
if i < uri.len and uri[i] == '?':
i.inc # Skip '?'
i.inc parseUntil(uri, result.query, {'#'}, i)
if uri[i] == '#':
if i < uri.len and uri[i] == '#':
i.inc # Skip '#'
i.inc parseUntil(uri, result.anchor, {}, i)
@@ -156,7 +155,7 @@ proc parseUri*(uri: string, result: var Uri) =
# Check if this is a reference URI (relative URI)
let doubleSlash = uri.len > 1 and uri[1] == '/'
if uri[i] == '/':
if i < uri.len and uri[i] == '/':
# Make sure ``uri`` doesn't begin with '//'.
if not doubleSlash:
parsePath(uri, i, result)
@@ -164,7 +163,7 @@ proc parseUri*(uri: string, result: var Uri) =
# Scheme
i.inc parseWhile(uri, result.scheme, Letters + Digits + {'+', '-', '.'}, i)
if uri[i] != ':' and not doubleSlash:
if (i >= uri.len or uri[i] != ':') and not doubleSlash:
# Assume this is a reference URI (relative URI)
i = 0
result.scheme.setLen(0)
@@ -174,7 +173,7 @@ proc parseUri*(uri: string, result: var Uri) =
i.inc # Skip ':'
# Authority
if uri[i] == '/' and uri[i+1] == '/':
if i+1 < uri.len and uri[i] == '/' and uri[i+1] == '/':
i.inc(2) # Skip //
var authority = ""
i.inc parseUntil(uri, authority, {'/', '?', '#'}, i)
@@ -197,13 +196,13 @@ proc removeDotSegments(path: string): string =
let endsWithSlash = path[path.len-1] == '/'
var i = 0
var currentSegment = ""
while true:
while i < path.len:
case path[i]
of '/':
collection.add(currentSegment)
currentSegment = ""
of '.':
if path[i+1] == '.' and path[i+2] == '/':
if i+2 < path.len and path[i+1] == '.' and path[i+2] == '/':
if collection.len > 0:
discard collection.pop()
i.inc 3
@@ -212,13 +211,11 @@ proc removeDotSegments(path: string): string =
i.inc 2
continue
currentSegment.add path[i]
of '\0':
if currentSegment != "":
collection.add currentSegment
break
else:
currentSegment.add path[i]
i.inc
if currentSegment != "":
collection.add currentSegment
result = collection.join("/")
if endsWithSlash: result.add '/'
@@ -320,18 +317,18 @@ proc `/`*(x: Uri, path: string): Uri =
result = x
if result.path.len == 0:
if path[0] != '/':
if path.len == 0 or path[0] != '/':
result.path = "/"
result.path.add(path)
return
if result.path[result.path.len-1] == '/':
if path[0] == '/':
if result.path.len > 0 and result.path[result.path.len-1] == '/':
if path.len > 0 and path[0] == '/':
result.path.add(path[1 .. path.len-1])
else:
result.path.add(path)
else:
if path[0] != '/':
if path.len == 0 or path[0] != '/':
result.path.add '/'
result.path.add(path)
@@ -373,7 +370,7 @@ when isMainModule:
const test1 = "abc\L+def xyz"
doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz"
doAssert decodeUrl(encodeUrl(test1)) == test1
block:
let str = "http://localhost"
let test = parseUri(str)
@@ -464,7 +461,7 @@ when isMainModule:
doAssert test.hostname == "github.com"
doAssert test.port == "dom96"
doAssert test.path == "/packages"
block:
let str = "file:///foo/bar/baz.txt"
let test = parseUri(str)

View File

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

View File

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

View File

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

View File

@@ -56,7 +56,7 @@ var
# list of exception handlers
# a global variable for the root of all try blocks
currException {.threadvar.}: ref Exception
raise_counter {.threadvar.}: uint
raise_counter {.threadvar.}: uint
gcFramePtr {.threadvar.}: GcFrame
@@ -126,10 +126,10 @@ proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
while cur != nil and cur.raise_id != id:
prev = cur
cur = cur.up
if cur == nil:
if cur == nil:
showErrorMessage("popCurrentExceptionEx() exception was not found in the exception stack. Aborting...")
quitOrDebug()
prev.up = cur.up
prev.up = cur.up
# some platforms have native support for stack traces:
const
@@ -503,7 +503,7 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
registerSignalHandler() # call it in initialization section
proc setControlCHook(hook: proc () {.noconv.} not nil) =
proc setControlCHook(hook: proc () {.noconv.}) =
# ugly cast, but should work on all architectures:
type SignalHandler = proc (sign: cint) {.noconv, benign.}
c_signal(SIGINT, cast[SignalHandler](hook))

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -209,7 +209,7 @@ proc `$`*(data: Tcommandline_results): string =
# - Parse code
template raise_or_quit(exception, message: expr): stmt {.immediate.} =
template raise_or_quit(exception, message: untyped) =
## Avoids repeating if check based on the default quit_on_failure variable.
##
## As a special case, if message has a zero length the call to quit won't
@@ -230,15 +230,15 @@ template run_custom_proc(parsed_parameter: Tparsed_parameter,
##
## Pass in the string of the parameter triggering the call. If the
if not custom_validator.isNil:
try:
let message = custom_validator(parameter, parsed_parameter)
if not message.isNil and message.len > 0:
raise_or_quit(ValueError, ("Failed to validate value for " &
"parameter $1:\n$2" % [escape(parameter), message]))
except:
raise_or_quit(ValueError, ("Couldn't run custom proc for " &
"parameter $1:\n$2" % [escape(parameter),
getCurrentExceptionMsg()]))
let message = custom_validator(parameter, parsed_parameter)
if not message.isNil and message.len > 0:
raise_or_quit(ValueError, ("Failed to validate value for " &
"parameter $1:\n$2" % [escape(parameter), message]))
proc parse_parameter(quit_on_failure: bool, param, value: string,
param_kind: Tparam_kind): Tparsed_parameter =

View File

@@ -388,13 +388,13 @@ type
cdecl.}
##cp property emulators
template defGetter(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
template defGetter(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
proc `get procName`*(obj: otype): memberType {.cdecl.} =
return obj.memberName
template defSetter(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
template defSetter(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
proc `set procName`*(obj: otype, value: memberType) {.cdecl.} =
obj.memberName = value
template defProp(otype: typedesc, memberType: typedesc, memberName: expr, procName: expr): stmt {.immediate.} =
template defProp(otype: typedesc, memberType: typedesc, memberName, procName: untyped) =
defGetter(otype, memberType, memberName, procName)
defSetter(otype, memberType, memberName, procName)
@@ -908,7 +908,7 @@ proc getShapes*(arb: PArbiter, a, b: var PShape) {.inline.} =
#/ A macro shortcut for defining and retrieving the shapes from an arbiter.
#define CP_ARBITER_GET_SHAPES(arb, a, b) cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b);
template getShapes*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
template getShapes*(arb: PArbiter, name1, name2: untyped) =
var name1, name2: PShape
getShapes(arb, name1, name2)
@@ -923,7 +923,7 @@ template getShapes*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
#/ A macro shortcut for defining and retrieving the bodies from an arbiter.
#define CP_ARBITER_GET_BODIES(arb, a, b) cpBody *a, *b; cpArbiterGetBodies(arb, &a, &b);
template getBodies*(arb: PArbiter, name1, name2: expr): stmt {.immediate.} =
template getBodies*(arb: PArbiter, name1, name2: untyped) =
var name1, name2: PBOdy
getBodies(arb, name1, name2)
@@ -947,11 +947,11 @@ proc getDepth*(arb: PArbiter; i: cint): CpFloat {.
cdecl, importc: "cpArbiterGetDepth", dynlib: Lib.}
##Shapes
template defShapeSetter(memberType: typedesc, memberName: expr, procName: expr, activates: bool): stmt {.immediate.} =
template defShapeSetter(memberType: typedesc, memberName: untyped, procName: untyped, activates: bool) =
proc `set procName`*(obj: PShape, value: memberType) {.cdecl.} =
if activates and obj.body != nil: obj.body.activate()
obj.memberName = value
template defShapeProp(memberType: typedesc, memberName: expr, procName: expr, activates: bool): stmt {.immediate.} =
template defShapeProp(memberType: typedesc, memberName: untyped, procName: untyped, activates: bool) =
defGetter(PShape, memberType, memberName, procName)
defShapeSetter(memberType, memberName, procName, activates)
@@ -1272,11 +1272,11 @@ proc activateBodies(constraint: PConstraint) {.inline.} =
# cpConstraintActivateBodies(constraint); \
# constraint->member = value; \
# }
template defConstraintSetter(memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
template defConstraintSetter(memberType: typedesc, member, name: untyped) =
proc `set name`*(constraint: PConstraint, value: memberType) {.cdecl.} =
activateBodies(constraint)
constraint.member = value
template defConstraintProp(memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
template defConstraintProp(memberType: typedesc, member, name: untyped) =
defGetter(PConstraint, memberType, member, name)
defConstraintSetter(memberType, member, name)
# CP_DefineConstraintStructGetter(cpSpace*, CP_PRIVATE(space), Space)
@@ -1306,18 +1306,18 @@ proc getImpulse*(constraint: PConstraint): CpFloat {.inline.} =
# cpConstraintActivateBodies(constraint); \
# ((struct *)constraint)->member = value; \
# }
template constraintCheckCast(constraint: PConstraint, ctype: expr): stmt {.immediate.} =
template constraintCheckCast(constraint: PConstraint, ctype: untyped) =
assert(constraint.klass == `ctype getClass`(), "Constraint is the wrong class")
template defCGetter(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
template defCGetter(ctype: untyped, memberType: typedesc, member, name: untyped) =
proc `get ctype name`*(constraint: PConstraint): memberType {.cdecl.} =
constraintCheckCast(constraint, ctype)
result = cast[`P ctype`](constraint).member
template defCSetter(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
template defCSetter(ctype: untyped, memberType: typedesc, member, name: untyped) =
proc `set ctype name`*(constraint: PConstraint, value: memberType) {.cdecl.} =
constraintCheckCast(constraint, ctype)
activateBodies(constraint)
cast[`P ctype`](constraint).member = value
template defCProp(ctype: expr, memberType: typedesc, member: expr, name: expr): stmt {.immediate.} =
template defCProp(ctype: untyped, memberType: typedesc, member, name: untyped) =
defCGetter(ctype, memberType, member, name)
defCSetter(ctype, memberType, member, name)

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ task "install", "compile and install nake binary":
for index, dir in pairs(path):
echo " ", index, ". ", dir
echo "Where to install nake binary? (quit with ^C or quit or exit)"
let ans = stdin.readLine().toLower
let ans = stdin.readLine().toLowerAscii
var index = 0
case ans
of "q", "quit", "x", "exit":

View File

@@ -78,7 +78,7 @@ proc write*(buffer: PBuffer; val: var string) =
setLen buffer.data, buffer.pos + length.int
copyMem(addr buffer.data[buffer.pos], addr val[0], length.int)
inc buffer.pos, length.int
proc write*[T: TNumber|bool|char|byte](buffer: PBuffer; val: T) =
proc write*[T: SomeNumber|bool|char|byte](buffer: PBuffer; val: T) =
var v: T
shallowCopy v, val
writeBE buffer, v

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -46,7 +46,7 @@ type
type1 = typedesc
type2 = typedesc
proc typePairs(A, B: type1; C, D: type2) = nil
proc typePairs(A, B: type1; C, D: type2) = discard
accept typePairs(int, int, TFoo, TFOO)
accept typePairs(TBAR, TBar, TBAR, TBAR)
@@ -55,7 +55,7 @@ accept typePairs(int, int, string, string)
reject typePairs(TBAR, TBar, TBar, TFoo)
reject typePairs(string, int, TBAR, TBAR)
proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = nil
proc typePairs2[T: typedesc, U: typedesc](A, B: T; C, D: U) = discard
accept typePairs2(int, int, TFoo, TFOO)
accept typePairs2(TBAR, TBar, TBAR, TBAR)
@@ -71,12 +71,12 @@ proc dontBind(a: typedesc, b: typedesc) =
accept dontBind(int, float)
accept dontBind(TFoo, TFoo)
proc dontBind2(a, b: typedesc) = nil
proc dontBind2(a, b: typedesc) = discard
accept dontBind2(int, float)
accept dontBind2(TBar, int)
proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = nil
proc bindArg(T: typedesc, U: typedesc, a, b: T, c, d: U) = discard
accept bindArg(int, string, 10, 20, "test", "nest")
accept bindArg(int, int, 10, 20, 30, 40)

View File

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

View File

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

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
{.experimental: "notnil".}
type
Fields = enum

View File

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

View File

@@ -4,7 +4,7 @@ discard """
"""
import strutils
{.experimental: "notnil".}
type
TObj = object
@@ -18,13 +18,13 @@ proc q(s: superstring) =
echo s
proc p2() =
var a: string = "I am not nil"
var a: string = "I am not nil"
q(a) # but this should and does not
p2()
proc q(x: pointer not nil) =
nil
discard
proc p() =
var x: pointer

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

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