Merge branch 'devel' into range_float_type

This commit is contained in:
cooldome 2018-04-30 23:57:33 +01:00
commit d21693f5e4
48 changed files with 424 additions and 2372 deletions

View file

@ -83,6 +83,11 @@
- ``nil`` for strings/seqs is finally gone. Instead the default value for - ``nil`` for strings/seqs is finally gone. Instead the default value for
these is ``"" / @[]``. 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.
- Range float types, example `range[0.0 .. Inf]`. More details in language manual. - Range float types, example `range[0.0 .. Inf]`. More details in language manual.

View file

@ -63,6 +63,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
of tyNil: of tyNil:
result = genNilStringLiteral(p.module, n.info) result = genNilStringLiteral(p.module, n.info)
of tyString: of tyString:
# with the new semantics for 'nil' strings, we can map "" to nil and
# save tons of allocations:
#if n.strVal.len == 0: result = genNilStringLiteral(p.module, n.info)
#else:
result = genStringLiteral(p.module, n) result = genStringLiteral(p.module, n)
else: else:
if n.strVal.isNil: result = rope("NIM_NIL") if n.strVal.isNil: result = rope("NIM_NIL")
@ -905,7 +909,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
if ty.kind in {tyRef, tyPtr}: if ty.kind in {tyRef, tyPtr}:
ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check:
if optBoundsCheck in p.options: if optBoundsCheck in p.options:
if ty.kind == tyString: if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options):
linefmt(p, cpsStmts, linefmt(p, cpsStmts,
"if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n", "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n",
rdLoc(b), rdLoc(a), lenField(p)) rdLoc(b), rdLoc(a), lenField(p))

View file

@ -188,11 +188,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
if i < len(arg) and (arg[i] in {':', '='}): inc(i) if i < len(arg) and (arg[i] in {':', '='}): inc(i)
else: invalidCmdLineOption(pass, orig, info) else: invalidCmdLineOption(pass, orig, info)
if state == wHint: if state == wHint:
var x = findStr(msgs.HintsToStr, id) let x = findStr(msgs.HintsToStr, id)
if x >= 0: n = TNoteKind(x + ord(hintMin)) if x >= 0: n = TNoteKind(x + ord(hintMin))
else: localError(info, "unknown hint: " & id) else: localError(info, "unknown hint: " & id)
else: else:
var x = findStr(msgs.WarningsToStr, id) let x = findStr(msgs.WarningsToStr, id)
if x >= 0: n = TNoteKind(x + ord(warnMin)) if x >= 0: n = TNoteKind(x + ord(warnMin))
else: localError(info, "unknown warning: " & id) else: localError(info, "unknown warning: " & id)
case substr(arg, i).normalize case substr(arg, i).normalize
@ -499,6 +499,7 @@ proc processSwitch(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
undefSymbol("nimOldNewlines") undefSymbol("nimOldNewlines")
else: else:
localError(info, errOnOrOffExpectedButXFound, arg) localError(info, errOnOrOffExpectedButXFound, arg)
of "laxstrings": processOnOffSwitch({optLaxStrings}, arg, pass, info)
of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info) of "checks", "x": processOnOffSwitch(ChecksOptions, arg, pass, info)
of "floatchecks": of "floatchecks":
processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info) processOnOffSwitch({optNaNCheck, optInfCheck}, arg, pass, info)

View file

@ -115,3 +115,4 @@ proc initDefines*() =
defineSymbol("nimSymKind") defineSymbol("nimSymKind")
defineSymbol("nimVmEqIdent") defineSymbol("nimVmEqIdent")
defineSymbol("nimNoNil") defineSymbol("nimNoNil")
defineSymbol("nimNoZeroTerminator")

View file

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

View file

@ -477,6 +477,10 @@ const
hintMin* = hintSuccess hintMin* = hintSuccess
hintMax* = high(TMsgKind) hintMax* = high(TMsgKind)
static:
doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
type type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind] TNoteKinds* = set[TNoteKind]

View file

@ -37,7 +37,8 @@ type # please make sure we have under 32 options
# evaluation # evaluation
optPatterns, # en/disable pattern matching optPatterns, # en/disable pattern matching
optMemTracker, optMemTracker,
optHotCodeReloading optHotCodeReloading,
optLaxStrings
TOptions* = set[TOption] TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible** TGlobalOption* = enum # **keep binary compatible**

View file

@ -1064,6 +1064,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
if n.len > 1: if n.len > 1:
let opr = if n[0].kind == nkIdent: n[0].ident let opr = if n[0].kind == nkIdent: n[0].ident
elif n[0].kind == nkSym: n[0].sym.name elif n[0].kind == nkSym: n[0].sym.name
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
else: nil else: nil
if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)): if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
put(g, tkSpaces, Space) put(g, tkSpaces, Space)

View file

@ -251,7 +251,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
while true: while true:
j = j * 10 + ord(frmt[i]) - ord('0') j = j * 10 + ord(frmt[i]) - ord('0')
inc(i) inc(i)
if frmt[i] notin {'0'..'9'}: break if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
num = j num = j
if j > high(args) + 1: if j > high(args) + 1:
errorHandler(rInvalidFormatStr, $(j)) errorHandler(rInvalidFormatStr, $(j))

View file

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

View file

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

View file

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

View file

@ -960,11 +960,7 @@ enforced. For example, when reading strings from binary files, they are merely
a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of
``s``, not the i-th *unichar*. ``s``, not the i-th *unichar*.
String variables are initialized with a special value, called ``nil``. However, String variables are initialized with the empty strings ``""``.
most string operations cannot deal with ``nil`` (leading to an exception being
raised) for performance reasons. It is best to use empty strings ``""``
rather than ``nil`` as the *empty* value. But ``""`` often creates a string
object on the heap, so there is a trade-off to be made here.
Integers Integers
@ -1309,11 +1305,7 @@ Example:
x: seq[int] # a reference to a sequence of integers x: seq[int] # a reference to a sequence of integers
x = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence allocated on the heap x = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence allocated on the heap
Sequence variables are initialized with ``nil``. However, most sequence Sequence variables are initialized with ``@[]``.
operations cannot deal with ``nil`` (leading to an exception being
raised) for performance reasons. Thus one should use empty sequences ``@[]``
rather than ``nil`` as the *empty* value. But ``@[]`` creates a sequence
object on the heap, so there is a trade-off to be made here.
The ``for`` statement can be used with one or two variables when used with a The ``for`` statement can be used with one or two variables when used with a
sequence. When you use the one variable form, the variable will hold the value sequence. When you use the one variable form, the variable will hold the value
@ -1355,11 +1347,9 @@ type does not matter.
.. code-block:: nim .. code-block:: nim
:test: "nim c $1" :test: "nim c $1"
var var
fruits: seq[string] # reference to a sequence of strings that is initialized with 'nil' fruits: seq[string] # reference to a sequence of strings that is initialized with '@[]'
capitals: array[3, string] # array of strings with a fixed size capitals: array[3, string] # array of strings with a fixed size
fruits = @[] # creates an empty sequence on the heap that will be referenced by 'fruits'
capitals = ["New York", "London", "Berlin"] # array 'capitals' allows assignment of only three elements capitals = ["New York", "London", "Berlin"] # array 'capitals' allows assignment of only three elements
fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime fruits.add("Banana") # sequence 'fruits' is dynamically expandable during runtime
fruits.add("Mango") fruits.add("Mango")
@ -1691,7 +1681,7 @@ rules apply:
write(stdout, x(3)) # no error: A.x is called write(stdout, x(3)) # no error: A.x is called
write(stdout, x("")) # no error: B.x is called write(stdout, x("")) # no error: B.x is called
proc x*(a: int): string = nil proc x*(a: int): string = discard
write(stdout, x(3)) # ambiguous: which `x` is to call? write(stdout, x(3)) # ambiguous: which `x` is to call?

View file

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

View file

@ -10,7 +10,7 @@
from pcre import nil from pcre import nil
import nre.private.util import nre.private.util
import tables import tables
from strutils import toLower, `%` from strutils import `%`
from math import ceil from math import ceil
import options import options
from unicode import runeLenAt from unicode import runeLenAt

View file

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

View file

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

View file

@ -134,7 +134,7 @@ proc decode*(s: string): string =
i = 0 i = 0
r = 0 r = 0
while true: while true:
while s[i] in Whitespace: inc(i) while i < s.len and s[i] in Whitespace: inc(i)
if i < s.len-3: if i < s.len-3:
let let
a = s[i].decodeByte a = s[i].decodeByte

View file

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

View file

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

View file

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

View file

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

View file

@ -247,7 +247,7 @@ proc parseChunks(s: Socket, timeout: int): string =
var i = 0 var i = 0
if chunkSizeStr == "": if chunkSizeStr == "":
httpError("Server terminated connection prematurely") httpError("Server terminated connection prematurely")
while true: while i < chunkSizeStr.len:
case chunkSizeStr[i] case chunkSizeStr[i]
of '0'..'9': of '0'..'9':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0')) chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
@ -255,8 +255,6 @@ proc parseChunks(s: Socket, timeout: int): string =
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10) chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
of 'A'..'F': of 'A'..'F':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10) chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
of '\0':
break
of ';': of ';':
# http://tools.ietf.org/html/rfc2616#section-3.6.1 # http://tools.ietf.org/html/rfc2616#section-3.6.1
# We don't care about chunk-extensions. # We don't care about chunk-extensions.
@ -938,7 +936,7 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void]
var i = 0 var i = 0
if chunkSizeStr == "": if chunkSizeStr == "":
httpError("Server terminated connection prematurely") httpError("Server terminated connection prematurely")
while true: while i < chunkSizeStr.len:
case chunkSizeStr[i] case chunkSizeStr[i]
of '0'..'9': of '0'..'9':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0')) chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0'))
@ -946,8 +944,6 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[void]
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10) chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10)
of 'A'..'F': of 'A'..'F':
chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10) chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10)
of '\0':
break
of ';': of ';':
# http://tools.ietf.org/html/rfc2616#section-3.6.1 # http://tools.ietf.org/html/rfc2616#section-3.6.1
# We don't care about chunk-extensions. # We don't care about chunk-extensions.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -562,7 +562,7 @@ proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
result.useSound = importSound(data[2], errors, "useSound") result.useSound = importSound(data[2], errors, "useSound")
case data[1].str.toLower case data[1].str.toLowerAscii
of "projectile": of "projectile":
result.kind = Projectile result.kind = Projectile
if data[2]["bullet"].kind == JString: if data[2]["bullet"].kind == JString:

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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