Merge branch 'devel' of https://github.com/Araq/Nimrod into devel
This commit is contained in:
commit
821fe72ff5
11 changed files with 549 additions and 128 deletions
|
|
@ -4970,9 +4970,9 @@ or ``ref T`` or ``ptr T`` this means no locations are modified. It is a static
|
||||||
error to mark a proc/iterator to have no side effect if the compiler cannot
|
error to mark a proc/iterator to have no side effect if the compiler cannot
|
||||||
verify this.
|
verify this.
|
||||||
|
|
||||||
As a special semantic rule, the built-in ``debugEcho`` pretends to be free of
|
As a special semantic rule, the built-in `debugEcho <system.html#debugEcho>`_
|
||||||
side effects, so that it can be used for debugging routines marked as
|
pretends to be free of side effects, so that it can be used for debugging
|
||||||
``noSideEffect``.
|
routines marked as ``noSideEffect``.
|
||||||
|
|
||||||
**Future directions**: ``func`` may become a keyword and syntactic sugar for a
|
**Future directions**: ``func`` may become a keyword and syntactic sugar for a
|
||||||
proc with no side effects:
|
proc with no side effects:
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,7 @@ proc keepIf*[T](seq1: var seq[T], pred: proc(item: T): bool {.closure.}) =
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
## var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
|
## var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1]
|
||||||
## filter(floats, proc(x: float): bool = x > 10)
|
## keepIf(floats, proc(x: float): bool = x > 10)
|
||||||
## assert floats == @[13.0, 12.5, 10.1]
|
## assert floats == @[13.0, 12.5, 10.1]
|
||||||
var pos = 0
|
var pos = 0
|
||||||
for i in 0 .. <len(seq1):
|
for i in 0 .. <len(seq1):
|
||||||
|
|
@ -388,6 +388,7 @@ template mapIt*(seq1, typ, pred: expr): expr =
|
||||||
## let
|
## let
|
||||||
## nums = @[1, 2, 3, 4]
|
## nums = @[1, 2, 3, 4]
|
||||||
## strings = nums.mapIt(string, $(4 * it))
|
## strings = nums.mapIt(string, $(4 * it))
|
||||||
|
## assert strings == @["4", "8", "12", "16"]
|
||||||
var result {.gensym.}: seq[typ] = @[]
|
var result {.gensym.}: seq[typ] = @[]
|
||||||
for it {.inject.} in items(seq1):
|
for it {.inject.} in items(seq1):
|
||||||
result.add(pred)
|
result.add(pred)
|
||||||
|
|
|
||||||
|
|
@ -350,7 +350,8 @@ proc next*(c: var TCfgParser): TCfgEvent {.rtl, extern: "npc$1".} =
|
||||||
rawGetTok(c, c.tok)
|
rawGetTok(c, c.tok)
|
||||||
if c.tok.kind == tkBracketRi:
|
if c.tok.kind == tkBracketRi:
|
||||||
rawGetTok(c, c.tok)
|
rawGetTok(c, c.tok)
|
||||||
else:
|
else:
|
||||||
|
reset(result)
|
||||||
result.kind = cfgError
|
result.kind = cfgError
|
||||||
result.msg = errorStr(c, "']' expected, but found: " & c.tok.literal)
|
result.msg = errorStr(c, "']' expected, but found: " & c.tok.literal)
|
||||||
of tkInvalid, tkEquals, tkColon, tkBracketRi:
|
of tkInvalid, tkEquals, tkColon, tkBracketRi:
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ type
|
||||||
scheme, username, password,
|
scheme, username, password,
|
||||||
hostname, port, path, query, anchor: string]
|
hostname, port, path, query, anchor: string]
|
||||||
|
|
||||||
proc parseUrl*(url: string): TUrl =
|
proc parseUrl*(url: string): TUrl {.deprecated.} =
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
||||||
var scheme, username, password: string = ""
|
var scheme, username, password: string = ""
|
||||||
|
|
@ -86,7 +86,7 @@ proc parseUrl*(url: string): TUrl =
|
||||||
|
|
||||||
return (scheme, username, password, hostname, port, path, query, anchor)
|
return (scheme, username, password, hostname, port, path, query, anchor)
|
||||||
|
|
||||||
proc `$`*(u: TUrl): string =
|
proc `$`*(u: TUrl): string {.deprecated.} =
|
||||||
## turns the URL `u` into its string representation.
|
## turns the URL `u` into its string representation.
|
||||||
result = ""
|
result = ""
|
||||||
if u.scheme.len > 0:
|
if u.scheme.len > 0:
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,11 @@ const
|
||||||
## the set of characters a newline terminator can start with
|
## the set of characters a newline terminator can start with
|
||||||
|
|
||||||
AllChars* = {'\x00'..'\xFF'}
|
AllChars* = {'\x00'..'\xFF'}
|
||||||
## A set with all the possible characters. Not very useful by its own, you
|
## A set with all the possible characters.
|
||||||
## can use it to create *inverted* sets to make the ``find()`` proc find
|
##
|
||||||
## **invalid** characters in strings. Example:
|
## Not very useful by its own, you can use it to create *inverted* sets to
|
||||||
|
## make the `find() proc <#find,string,set[char],int>`_ find **invalid**
|
||||||
|
## characters in strings. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
## let invalid = AllChars - Digits
|
## let invalid = AllChars - Digits
|
||||||
|
|
@ -59,8 +61,11 @@ const
|
||||||
|
|
||||||
proc toLower*(c: char): char {.noSideEffect, procvar,
|
proc toLower*(c: char): char {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuToLowerChar".} =
|
rtl, extern: "nsuToLowerChar".} =
|
||||||
## Converts `c` into lower case. This works only for the letters A-Z.
|
## Converts `c` into lower case.
|
||||||
## See `unicode.toLower` for a version that works for any Unicode character.
|
##
|
||||||
|
## This works only for the letters ``A-Z``. See `unicode.toLower
|
||||||
|
## <unicode.html#toLower>`_ for a version that works for any Unicode
|
||||||
|
## character.
|
||||||
if c in {'A'..'Z'}:
|
if c in {'A'..'Z'}:
|
||||||
result = chr(ord(c) + (ord('a') - ord('A')))
|
result = chr(ord(c) + (ord('a') - ord('A')))
|
||||||
else:
|
else:
|
||||||
|
|
@ -68,16 +73,22 @@ proc toLower*(c: char): char {.noSideEffect, procvar,
|
||||||
|
|
||||||
proc toLower*(s: string): string {.noSideEffect, procvar,
|
proc toLower*(s: string): string {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuToLowerStr".} =
|
rtl, extern: "nsuToLowerStr".} =
|
||||||
## Converts `s` into lower case. This works only for the letters A-Z.
|
## Converts `s` into lower case.
|
||||||
## See `unicode.toLower` for a version that works for any Unicode character.
|
##
|
||||||
|
## This works only for the letters ``A-Z``. See `unicode.toLower
|
||||||
|
## <unicode.html#toLower>`_ for a version that works for any Unicode
|
||||||
|
## character.
|
||||||
result = newString(len(s))
|
result = newString(len(s))
|
||||||
for i in 0..len(s) - 1:
|
for i in 0..len(s) - 1:
|
||||||
result[i] = toLower(s[i])
|
result[i] = toLower(s[i])
|
||||||
|
|
||||||
proc toUpper*(c: char): char {.noSideEffect, procvar,
|
proc toUpper*(c: char): char {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuToUpperChar".} =
|
rtl, extern: "nsuToUpperChar".} =
|
||||||
## Converts `c` into upper case. This works only for the letters a-z.
|
## Converts `c` into upper case.
|
||||||
## See `unicode.toUpper` for a version that works for any Unicode character.
|
##
|
||||||
|
## This works only for the letters ``A-Z``. See `unicode.toUpper
|
||||||
|
## <unicode.html#toUpper>`_ for a version that works for any Unicode
|
||||||
|
## character.
|
||||||
if c in {'a'..'z'}:
|
if c in {'a'..'z'}:
|
||||||
result = chr(ord(c) - (ord('a') - ord('A')))
|
result = chr(ord(c) - (ord('a') - ord('A')))
|
||||||
else:
|
else:
|
||||||
|
|
@ -85,8 +96,11 @@ proc toUpper*(c: char): char {.noSideEffect, procvar,
|
||||||
|
|
||||||
proc toUpper*(s: string): string {.noSideEffect, procvar,
|
proc toUpper*(s: string): string {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuToUpperStr".} =
|
rtl, extern: "nsuToUpperStr".} =
|
||||||
## Converts `s` into upper case. This works only for the letters a-z.
|
## Converts `s` into upper case.
|
||||||
## See `unicode.toUpper` for a version that works for any Unicode character.
|
##
|
||||||
|
## This works only for the letters ``A-Z``. See `unicode.toUpper
|
||||||
|
## <unicode.html#toUpper>`_ for a version that works for any Unicode
|
||||||
|
## character.
|
||||||
result = newString(len(s))
|
result = newString(len(s))
|
||||||
for i in 0..len(s) - 1:
|
for i in 0..len(s) - 1:
|
||||||
result[i] = toUpper(s[i])
|
result[i] = toUpper(s[i])
|
||||||
|
|
@ -94,13 +108,16 @@ proc toUpper*(s: string): string {.noSideEffect, procvar,
|
||||||
proc capitalize*(s: string): string {.noSideEffect, procvar,
|
proc capitalize*(s: string): string {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuCapitalize".} =
|
rtl, extern: "nsuCapitalize".} =
|
||||||
## 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 = toUpper(s[0]) & substr(s, 1)
|
result = toUpper(s[0]) & substr(s, 1)
|
||||||
|
|
||||||
proc normalize*(s: string): string {.noSideEffect, procvar,
|
proc normalize*(s: string): string {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuNormalize".} =
|
rtl, extern: "nsuNormalize".} =
|
||||||
## Normalizes the string `s`. That means to convert it to lower case and
|
## Normalizes the string `s`.
|
||||||
## remove any '_'. This is needed for Nimrod identifiers for example.
|
##
|
||||||
|
## That means to convert it to lower case and remove any '_'. This is needed
|
||||||
|
## for Nimrod identifiers for example.
|
||||||
result = newString(s.len)
|
result = newString(s.len)
|
||||||
var j = 0
|
var j = 0
|
||||||
for i in 0..len(s) - 1:
|
for i in 0..len(s) - 1:
|
||||||
|
|
@ -155,6 +172,7 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
|
||||||
proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect,
|
proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect,
|
||||||
rtl, extern: "nsuStrip", operator: 5.} =
|
rtl, extern: "nsuStrip", operator: 5.} =
|
||||||
## Strips whitespace from `s` and returns the resulting string.
|
## Strips whitespace from `s` and returns the resulting string.
|
||||||
|
##
|
||||||
## If `leading` is true, leading whitespace is stripped.
|
## If `leading` is true, leading whitespace is stripped.
|
||||||
## If `trailing` is true, trailing whitespace is stripped.
|
## If `trailing` is true, trailing whitespace is stripped.
|
||||||
const
|
const
|
||||||
|
|
@ -169,8 +187,10 @@ proc strip*(s: string, leading = true, trailing = true): string {.noSideEffect,
|
||||||
result = substr(s, first, last)
|
result = substr(s, first, last)
|
||||||
|
|
||||||
proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
|
proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
|
||||||
## Converts a character `c` to its octal representation. The resulting
|
## Converts a character `c` to its octal representation.
|
||||||
## string may not have a leading zero. Its length is always exactly 3.
|
##
|
||||||
|
## The resulting string may not have a leading zero. Its length is always
|
||||||
|
## exactly 3.
|
||||||
result = newString(3)
|
result = newString(3)
|
||||||
var val = ord(c)
|
var val = ord(c)
|
||||||
for i in countdown(2, 0):
|
for i in countdown(2, 0):
|
||||||
|
|
@ -280,9 +300,11 @@ iterator split*(s: string, sep: string): string =
|
||||||
inc(last, sep.len)
|
inc(last, sep.len)
|
||||||
|
|
||||||
iterator splitLines*(s: string): string =
|
iterator splitLines*(s: string): string =
|
||||||
## Splits the string `s` into its containing lines. Every newline
|
## Splits the string `s` into its containing lines.
|
||||||
## combination (CR, LF, CR-LF) is supported. The result strings contain
|
##
|
||||||
## no trailing ``\n``.
|
## Every `character literal <manual.html#character-literals>`_ newline
|
||||||
|
## combination (CR, LF, CR-LF) is supported. The result strings contain no
|
||||||
|
## trailing ``\n``.
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
|
|
@ -315,13 +337,25 @@ iterator splitLines*(s: string): string =
|
||||||
|
|
||||||
proc splitLines*(s: string): seq[string] {.noSideEffect,
|
proc splitLines*(s: string): seq[string] {.noSideEffect,
|
||||||
rtl, extern: "nsuSplitLines".} =
|
rtl, extern: "nsuSplitLines".} =
|
||||||
## The same as the `splitLines` iterator, but is a proc that returns a
|
## The same as the `splitLines <#splitLines.i,string>`_ iterator, but is a
|
||||||
## sequence of substrings.
|
## proc that returns a sequence of substrings.
|
||||||
accumulateResult(splitLines(s))
|
accumulateResult(splitLines(s))
|
||||||
|
|
||||||
proc countLines*(s: string): int {.noSideEffect,
|
proc countLines*(s: string): int {.noSideEffect,
|
||||||
rtl, extern: "nsuCountLines".} =
|
rtl, extern: "nsuCountLines".} =
|
||||||
## same as ``len(splitLines(s))``, but much more efficient.
|
## Returns the number of new line separators in the string `s`.
|
||||||
|
##
|
||||||
|
## This is the same as ``len(splitLines(s))``, but much more efficient
|
||||||
|
## because it doesn't modify the string creating temporal objects. Every
|
||||||
|
## `character literal <manual.html#character-literals>`_ newline combination
|
||||||
|
## (CR, LF, CR-LF) is supported.
|
||||||
|
##
|
||||||
|
## Despite its name this proc might not actually return the *number of lines*
|
||||||
|
## in `s` because the concept of what a line is can vary. For example, a
|
||||||
|
## string like ``Hello world`` is a line of text, but the proc will return a
|
||||||
|
## value of zero because there are no newline separators. Also, text editors
|
||||||
|
## usually don't count trailing newline characters in a text file as a new
|
||||||
|
## empty line, but this proc will.
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < s.len:
|
while i < s.len:
|
||||||
case s[i]
|
case s[i]
|
||||||
|
|
@ -334,28 +368,30 @@ proc countLines*(s: string): int {.noSideEffect,
|
||||||
|
|
||||||
proc split*(s: string, seps: set[char] = Whitespace): seq[string] {.
|
proc split*(s: string, seps: set[char] = Whitespace): seq[string] {.
|
||||||
noSideEffect, rtl, extern: "nsuSplitCharSet".} =
|
noSideEffect, rtl, extern: "nsuSplitCharSet".} =
|
||||||
## The same as the `split` iterator, but is a proc that returns a
|
## The same as the `split iterator <#split.i,string,set[char]>`_, but is a
|
||||||
## sequence of substrings.
|
## proc that returns a sequence of substrings.
|
||||||
accumulateResult(split(s, seps))
|
accumulateResult(split(s, seps))
|
||||||
|
|
||||||
proc split*(s: string, sep: char): seq[string] {.noSideEffect,
|
proc split*(s: string, sep: char): seq[string] {.noSideEffect,
|
||||||
rtl, extern: "nsuSplitChar".} =
|
rtl, extern: "nsuSplitChar".} =
|
||||||
## The same as the `split` iterator, but is a proc that returns a sequence
|
## The same as the `split iterator <#split.i,string,char>`_, but is a proc
|
||||||
## of substrings.
|
## that returns a sequence of substrings.
|
||||||
accumulateResult(split(s, sep))
|
accumulateResult(split(s, sep))
|
||||||
|
|
||||||
proc split*(s: string, sep: string): seq[string] {.noSideEffect,
|
proc split*(s: string, sep: string): seq[string] {.noSideEffect,
|
||||||
rtl, extern: "nsuSplitString".} =
|
rtl, extern: "nsuSplitString".} =
|
||||||
## Splits the string `s` into substrings using a string separator.
|
## Splits the string `s` into substrings using a string separator.
|
||||||
##
|
##
|
||||||
## Substrings are separated by the string `sep`.
|
## Substrings are separated by the string `sep`. This is a wrapper around the
|
||||||
|
## `split iterator <#split.i,string,string>`_.
|
||||||
accumulateResult(split(s, sep))
|
accumulateResult(split(s, sep))
|
||||||
|
|
||||||
proc toHex*(x: BiggestInt, len: int): string {.noSideEffect,
|
proc toHex*(x: BiggestInt, len: int): string {.noSideEffect,
|
||||||
rtl, extern: "nsuToHex".} =
|
rtl, extern: "nsuToHex".} =
|
||||||
## Converts `x` to its hexadecimal representation. The resulting string
|
## Converts `x` to its hexadecimal representation.
|
||||||
## will be exactly `len` characters long. No prefix like ``0x``
|
##
|
||||||
## is generated. `x` is treated as an unsigned value.
|
## The resulting string will be exactly `len` characters long. No prefix like
|
||||||
|
## ``0x`` is generated. `x` is treated as an unsigned value.
|
||||||
const
|
const
|
||||||
HexChars = "0123456789ABCDEF"
|
HexChars = "0123456789ABCDEF"
|
||||||
var
|
var
|
||||||
|
|
@ -367,9 +403,10 @@ proc toHex*(x: BiggestInt, len: int): string {.noSideEffect,
|
||||||
|
|
||||||
proc intToStr*(x: int, minchars: int = 1): string {.noSideEffect,
|
proc intToStr*(x: int, minchars: int = 1): string {.noSideEffect,
|
||||||
rtl, extern: "nsuIntToStr".} =
|
rtl, extern: "nsuIntToStr".} =
|
||||||
## Converts `x` to its decimal representation. The resulting string
|
## Converts `x` to its decimal representation.
|
||||||
## will be minimally `minchars` characters long. This is achieved by
|
##
|
||||||
## adding leading zeros.
|
## The resulting string will be minimally `minchars` characters long. This is
|
||||||
|
## achieved by adding leading zeros.
|
||||||
result = $abs(x)
|
result = $abs(x)
|
||||||
for i in 1 .. minchars - len(result):
|
for i in 1 .. minchars - len(result):
|
||||||
result = '0' & result
|
result = '0' & result
|
||||||
|
|
@ -378,16 +415,18 @@ proc intToStr*(x: int, minchars: int = 1): string {.noSideEffect,
|
||||||
|
|
||||||
proc parseInt*(s: string): int {.noSideEffect, procvar,
|
proc parseInt*(s: string): int {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuParseInt".} =
|
rtl, extern: "nsuParseInt".} =
|
||||||
## Parses a decimal integer value contained in `s`. If `s` is not
|
## Parses a decimal integer value contained in `s`.
|
||||||
## a valid integer, `EInvalidValue` is raised.
|
##
|
||||||
|
## If `s` is not a valid integer, `EInvalidValue` is raised.
|
||||||
var L = parseutils.parseInt(s, result, 0)
|
var L = parseutils.parseInt(s, result, 0)
|
||||||
if L != s.len or L == 0:
|
if L != s.len or L == 0:
|
||||||
raise newException(EInvalidValue, "invalid integer: " & s)
|
raise newException(EInvalidValue, "invalid integer: " & s)
|
||||||
|
|
||||||
proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
|
proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuParseBiggestInt".} =
|
rtl, extern: "nsuParseBiggestInt".} =
|
||||||
## Parses a decimal integer value contained in `s`. If `s` is not
|
## Parses a decimal integer value contained in `s`.
|
||||||
## a valid integer, `EInvalidValue` is raised.
|
##
|
||||||
|
## If `s` is not a valid integer, `EInvalidValue` is raised.
|
||||||
var L = parseutils.parseBiggestInt(s, result, 0)
|
var L = parseutils.parseBiggestInt(s, result, 0)
|
||||||
if L != s.len or L == 0:
|
if L != s.len or L == 0:
|
||||||
raise newException(EInvalidValue, "invalid integer: " & s)
|
raise newException(EInvalidValue, "invalid integer: " & s)
|
||||||
|
|
@ -403,10 +442,11 @@ proc parseFloat*(s: string): float {.noSideEffect, procvar,
|
||||||
|
|
||||||
proc parseHexInt*(s: string): int {.noSideEffect, procvar,
|
proc parseHexInt*(s: string): int {.noSideEffect, procvar,
|
||||||
rtl, extern: "nsuParseHexInt".} =
|
rtl, extern: "nsuParseHexInt".} =
|
||||||
## Parses a hexadecimal integer value contained in `s`. If `s` is not
|
## Parses a hexadecimal integer value contained in `s`.
|
||||||
## a valid integer, `EInvalidValue` is raised. `s` can have one of the
|
##
|
||||||
## following optional prefixes: ``0x``, ``0X``, ``#``.
|
## If `s` is not a valid integer, `EInvalidValue` is raised. `s` can have one
|
||||||
## Underscores within `s` are ignored.
|
## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores
|
||||||
|
## 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 s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
|
||||||
elif s[i] == '#': inc(i)
|
elif s[i] == '#': inc(i)
|
||||||
|
|
@ -426,26 +466,32 @@ proc parseHexInt*(s: string): int {.noSideEffect, procvar,
|
||||||
else: raise newException(EInvalidValue, "invalid integer: " & s)
|
else: raise newException(EInvalidValue, "invalid integer: " & s)
|
||||||
|
|
||||||
proc parseBool*(s: string): bool =
|
proc parseBool*(s: string): bool =
|
||||||
## Parses a value into a `bool`. If ``s`` is one of the following values:
|
## Parses a value into a `bool`.
|
||||||
## ``y, yes, true, 1, on``, then returns `true`. If ``s`` is one of the
|
##
|
||||||
## following values: ``n, no, false, 0, off``, then returns `false`.
|
## If ``s`` is one of the following values: ``y, yes, true, 1, on``, then
|
||||||
## If ``s`` is something else a ``EInvalidValue`` exception is raised.
|
## returns `true`. If ``s`` is one of the following values: ``n, no, false,
|
||||||
|
## 0, off``, then returns `false`. If ``s`` is something else a
|
||||||
|
## ``EInvalidValue`` exception is raised.
|
||||||
case normalize(s)
|
case normalize(s)
|
||||||
of "y", "yes", "true", "1", "on": result = true
|
of "y", "yes", "true", "1", "on": result = true
|
||||||
of "n", "no", "false", "0", "off": result = false
|
of "n", "no", "false", "0", "off": result = false
|
||||||
else: raise newException(EInvalidValue, "cannot interpret as a bool: " & s)
|
else: raise newException(EInvalidValue, "cannot interpret as a bool: " & s)
|
||||||
|
|
||||||
proc parseEnum*[T: enum](s: string): T =
|
proc parseEnum*[T: enum](s: string): T =
|
||||||
## parses an enum ``T``. Raises ``EInvalidValue`` for an invalid value in
|
## Parses an enum ``T``.
|
||||||
## `s`. The comparison is done in a style insensitive way.
|
##
|
||||||
|
## Raises ``EInvalidValue`` for an invalid value in `s`. The comparison is
|
||||||
|
## done in a style insensitive way.
|
||||||
for e in low(T)..high(T):
|
for e in low(T)..high(T):
|
||||||
if cmpIgnoreStyle(s, $e) == 0:
|
if cmpIgnoreStyle(s, $e) == 0:
|
||||||
return e
|
return e
|
||||||
raise newException(EInvalidValue, "invalid enum value: " & s)
|
raise newException(EInvalidValue, "invalid enum value: " & s)
|
||||||
|
|
||||||
proc parseEnum*[T: enum](s: string, default: T): T =
|
proc parseEnum*[T: enum](s: string, default: T): T =
|
||||||
## parses an enum ``T``. Uses `default` for an invalid value in
|
## Parses an enum ``T``.
|
||||||
## `s`. The comparison is done in a style insensitive way.
|
##
|
||||||
|
## Uses `default` for an invalid value in `s`. The comparison is done in a
|
||||||
|
## style insensitive way.
|
||||||
for e in low(T)..high(T):
|
for e in low(T)..high(T):
|
||||||
if cmpIgnoreStyle(s, $e) == 0:
|
if cmpIgnoreStyle(s, $e) == 0:
|
||||||
return e
|
return e
|
||||||
|
|
@ -475,10 +521,11 @@ proc repeatStr*(count: int, s: string): string {.noSideEffect,
|
||||||
proc align*(s: string, count: int, padding = ' '): string {.
|
proc align*(s: string, count: int, padding = ' '): string {.
|
||||||
noSideEffect, rtl, extern: "nsuAlignString".} =
|
noSideEffect, rtl, extern: "nsuAlignString".} =
|
||||||
## Aligns a string `s` with `padding`, so that is of length `count`.
|
## Aligns a string `s` with `padding`, so that is of length `count`.
|
||||||
|
##
|
||||||
## `padding` characters (by default spaces) are added before `s` resulting in
|
## `padding` characters (by default spaces) are added before `s` resulting in
|
||||||
## right alignment. If ``s.len >= count``, no spaces are added and `s` is
|
## right alignment. If ``s.len >= count``, no spaces are added and `s` is
|
||||||
## returned unchanged. If you need to left align a string use the
|
## returned unchanged. If you need to left align a string use the `repeatChar
|
||||||
## ``repeatChar`` proc. Example:
|
## proc <#repeatChar>`_. Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
## assert align("abc", 4) == " abc"
|
## assert align("abc", 4) == " abc"
|
||||||
|
|
@ -532,7 +579,7 @@ proc wordWrap*(s: string, maxLineWidth = 80,
|
||||||
seps: set[char] = Whitespace,
|
seps: set[char] = Whitespace,
|
||||||
newLine = "\n"): string {.
|
newLine = "\n"): string {.
|
||||||
noSideEffect, rtl, extern: "nsuWordWrap".} =
|
noSideEffect, rtl, extern: "nsuWordWrap".} =
|
||||||
## word wraps `s`.
|
## Word wraps `s`.
|
||||||
result = newStringOfCap(s.len + s.len shr 6)
|
result = newStringOfCap(s.len + s.len shr 6)
|
||||||
var spaceLeft = maxLineWidth
|
var spaceLeft = maxLineWidth
|
||||||
var lastSep = ""
|
var lastSep = ""
|
||||||
|
|
@ -564,7 +611,7 @@ proc wordWrap*(s: string, maxLineWidth = 80,
|
||||||
|
|
||||||
proc unindent*(s: string, eatAllIndent = false): string {.
|
proc unindent*(s: string, eatAllIndent = false): string {.
|
||||||
noSideEffect, rtl, extern: "nsuUnindent".} =
|
noSideEffect, rtl, extern: "nsuUnindent".} =
|
||||||
## unindents `s`.
|
## Unindents `s`.
|
||||||
result = newStringOfCap(s.len)
|
result = newStringOfCap(s.len)
|
||||||
var i = 0
|
var i = 0
|
||||||
var pattern = true
|
var pattern = true
|
||||||
|
|
@ -592,6 +639,7 @@ proc unindent*(s: string, eatAllIndent = false): string {.
|
||||||
proc startsWith*(s, prefix: string): bool {.noSideEffect,
|
proc startsWith*(s, prefix: string): bool {.noSideEffect,
|
||||||
rtl, extern: "nsuStartsWith".} =
|
rtl, extern: "nsuStartsWith".} =
|
||||||
## Returns true iff ``s`` starts with ``prefix``.
|
## Returns true iff ``s`` starts with ``prefix``.
|
||||||
|
##
|
||||||
## If ``prefix == ""`` true is returned.
|
## If ``prefix == ""`` true is returned.
|
||||||
var i = 0
|
var i = 0
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -602,6 +650,7 @@ proc startsWith*(s, prefix: string): bool {.noSideEffect,
|
||||||
proc endsWith*(s, suffix: string): bool {.noSideEffect,
|
proc endsWith*(s, suffix: string): bool {.noSideEffect,
|
||||||
rtl, extern: "nsuEndsWith".} =
|
rtl, extern: "nsuEndsWith".} =
|
||||||
## Returns true iff ``s`` ends with ``suffix``.
|
## Returns true iff ``s`` ends with ``suffix``.
|
||||||
|
##
|
||||||
## If ``suffix == ""`` true is returned.
|
## If ``suffix == ""`` true is returned.
|
||||||
var i = 0
|
var i = 0
|
||||||
var j = len(s) - len(suffix)
|
var j = len(s) - len(suffix)
|
||||||
|
|
@ -613,6 +662,7 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect,
|
||||||
proc continuesWith*(s, substr: string, start: int): bool {.noSideEffect,
|
proc continuesWith*(s, substr: string, start: int): bool {.noSideEffect,
|
||||||
rtl, extern: "nsuContinuesWith".} =
|
rtl, extern: "nsuContinuesWith".} =
|
||||||
## Returns true iff ``s`` continues with ``substr`` at position ``start``.
|
## Returns true iff ``s`` continues with ``substr`` at position ``start``.
|
||||||
|
##
|
||||||
## If ``substr == ""`` true is returned.
|
## If ``substr == ""`` true is returned.
|
||||||
var i = 0
|
var i = 0
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -622,6 +672,8 @@ proc continuesWith*(s, substr: string, start: int): bool {.noSideEffect,
|
||||||
|
|
||||||
proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
||||||
inline.} =
|
inline.} =
|
||||||
|
## Adds a separator to `dest` only if its length is bigger than `startLen`.
|
||||||
|
##
|
||||||
## A shorthand for:
|
## A shorthand for:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: nimrod
|
||||||
|
|
@ -641,15 +693,15 @@ proc addSep*(dest: var string, sep = ", ", startLen = 0) {.noSideEffect,
|
||||||
if dest.len > startLen: add(dest, sep)
|
if dest.len > startLen: add(dest, sep)
|
||||||
|
|
||||||
proc allCharsInSet*(s: string, theSet: TCharSet): bool =
|
proc allCharsInSet*(s: string, theSet: TCharSet): bool =
|
||||||
## returns true iff each character of `s` is in the set `theSet`.
|
## Returns true iff each character of `s` is in the set `theSet`.
|
||||||
for c in items(s):
|
for c in items(s):
|
||||||
if c notin theSet: return false
|
if c notin theSet: return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc abbrev*(s: string, possibilities: openArray[string]): int =
|
proc abbrev*(s: string, possibilities: openArray[string]): int =
|
||||||
## returns the index of the first item in `possibilities` if not
|
## Returns the index of the first item in `possibilities` if not ambiguous.
|
||||||
## ambiguous; -1 if no item has been found; -2 if multiple items
|
##
|
||||||
## match.
|
## Returns -1 if no item has been found and -2 if multiple items match.
|
||||||
result = -1 # none found
|
result = -1 # none found
|
||||||
for i in 0..possibilities.len-1:
|
for i in 0..possibilities.len-1:
|
||||||
if possibilities[i].startsWith(s):
|
if possibilities[i].startsWith(s):
|
||||||
|
|
@ -663,7 +715,7 @@ proc abbrev*(s: string, possibilities: openArray[string]): int =
|
||||||
|
|
||||||
proc join*(a: openArray[string], sep: string): string {.
|
proc join*(a: openArray[string], sep: string): string {.
|
||||||
noSideEffect, rtl, extern: "nsuJoinSep".} =
|
noSideEffect, rtl, extern: "nsuJoinSep".} =
|
||||||
## concatenates all strings in `a` separating them with `sep`.
|
## Concatenates all strings in `a` separating them with `sep`.
|
||||||
if len(a) > 0:
|
if len(a) > 0:
|
||||||
var L = sep.len * (a.len-1)
|
var L = sep.len * (a.len-1)
|
||||||
for i in 0..high(a): inc(L, a[i].len)
|
for i in 0..high(a): inc(L, a[i].len)
|
||||||
|
|
@ -677,7 +729,7 @@ proc join*(a: openArray[string], sep: string): string {.
|
||||||
|
|
||||||
proc join*(a: openArray[string]): string {.
|
proc join*(a: openArray[string]): string {.
|
||||||
noSideEffect, rtl, extern: "nsuJoin".} =
|
noSideEffect, rtl, extern: "nsuJoin".} =
|
||||||
## concatenates all strings in `a`.
|
## Concatenates all strings in `a`.
|
||||||
if len(a) > 0:
|
if len(a) > 0:
|
||||||
var L = 0
|
var L = 0
|
||||||
for i in 0..high(a): inc(L, a[i].len)
|
for i in 0..high(a): inc(L, a[i].len)
|
||||||
|
|
@ -695,7 +747,7 @@ proc preprocessSub(sub: string, a: var TSkipTable) =
|
||||||
for i in 0..m-1: a[sub[i]] = m-i
|
for i in 0..m-1: a[sub[i]] = m-i
|
||||||
|
|
||||||
proc findAux(s, sub: string, start: int, a: TSkipTable): int =
|
proc findAux(s, sub: string, start: int, a: TSkipTable): int =
|
||||||
# fast "quick search" algorithm:
|
# Fast "quick search" algorithm:
|
||||||
var
|
var
|
||||||
m = len(sub)
|
m = len(sub)
|
||||||
n = len(s)
|
n = len(s)
|
||||||
|
|
@ -711,32 +763,36 @@ proc findAux(s, sub: string, start: int, a: TSkipTable): int =
|
||||||
|
|
||||||
proc find*(s, sub: string, start: int = 0): int {.noSideEffect,
|
proc find*(s, sub: string, start: int = 0): int {.noSideEffect,
|
||||||
rtl, extern: "nsuFindStr", operator: 6.} =
|
rtl, extern: "nsuFindStr", operator: 6.} =
|
||||||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
## Searches for `sub` in `s` starting at position `start`.
|
||||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
##
|
||||||
|
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||||
var a {.noinit.}: TSkipTable
|
var a {.noinit.}: TSkipTable
|
||||||
preprocessSub(sub, a)
|
preprocessSub(sub, a)
|
||||||
result = findAux(s, sub, start, a)
|
result = findAux(s, sub, start, a)
|
||||||
|
|
||||||
proc find*(s: string, sub: char, start: int = 0): int {.noSideEffect,
|
proc find*(s: string, sub: char, start: int = 0): int {.noSideEffect,
|
||||||
rtl, extern: "nsuFindChar".} =
|
rtl, extern: "nsuFindChar".} =
|
||||||
## Searches for `sub` in `s` starting at position `start`. Searching is
|
## Searches for `sub` in `s` starting at position `start`.
|
||||||
## case-sensitive. If `sub` is not in `s`, -1 is returned.
|
##
|
||||||
|
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||||
for i in start..len(s)-1:
|
for i in start..len(s)-1:
|
||||||
if sub == s[i]: return i
|
if sub == s[i]: return i
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc find*(s: string, chars: set[char], start: int = 0): int {.noSideEffect,
|
proc find*(s: string, chars: set[char], start: int = 0): int {.noSideEffect,
|
||||||
rtl, extern: "nsuFindCharSet".} =
|
rtl, extern: "nsuFindCharSet".} =
|
||||||
## Searches for `chars` in `s` starting at position `start`. If `s` contains
|
## Searches for `chars` in `s` starting at position `start`.
|
||||||
## none of the characters in `chars`, -1 is returned.
|
##
|
||||||
|
## If `s` contains none of the characters in `chars`, -1 is returned.
|
||||||
for i in start..s.len-1:
|
for i in start..s.len-1:
|
||||||
if s[i] in chars: return i
|
if s[i] in chars: return i
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} =
|
proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} =
|
||||||
## Searches for `sub` in `s` in reverse, starting at `start` and going
|
## Searches for `sub` in `s` in reverse, starting at `start` and going
|
||||||
## backwards to 0. Searching is case-sensitive. If `sub` is not in `s`, -1 is
|
## backwards to 0.
|
||||||
## returned.
|
##
|
||||||
|
## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
|
||||||
let realStart = if start == -1: s.len else: start
|
let realStart = if start == -1: s.len else: start
|
||||||
for i in countdown(realStart-sub.len, 0):
|
for i in countdown(realStart-sub.len, 0):
|
||||||
for j in 0..sub.len-1:
|
for j in 0..sub.len-1:
|
||||||
|
|
@ -748,10 +804,11 @@ proc rfind*(s, sub: string, start: int = -1): int {.noSideEffect.} =
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
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
|
||||||
## start with a quote, else returns `s`
|
## start with a quote, else returns `s`.
|
||||||
## DEPRECATED as it was confused for shell quoting function.
|
##
|
||||||
## For this application use osproc.quoteShell.
|
## **DEPRECATED** as it was confused for shell quoting function. For this
|
||||||
|
## application use `osproc.quoteShell <osproc.html#quoteShell>`_.
|
||||||
if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
|
if find(s, {' ', '\t'}) >= 0 and s[0] != '"':
|
||||||
result = '"' & s & '"'
|
result = '"' & s & '"'
|
||||||
else:
|
else:
|
||||||
|
|
@ -787,7 +844,9 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect,
|
||||||
|
|
||||||
proc replace*(s: string, sub, by: char): string {.noSideEffect,
|
proc replace*(s: string, sub, by: char): string {.noSideEffect,
|
||||||
rtl, extern: "nsuReplaceChar".} =
|
rtl, extern: "nsuReplaceChar".} =
|
||||||
## optimized version for characters.
|
## Replaces `sub` in `s` by the character `by`.
|
||||||
|
##
|
||||||
|
## Optimized version of `replace <#replace,string,string>`_ for characters.
|
||||||
result = newString(s.len)
|
result = newString(s.len)
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < s.len:
|
while i < s.len:
|
||||||
|
|
@ -797,9 +856,11 @@ proc replace*(s: string, sub, by: char): string {.noSideEffect,
|
||||||
|
|
||||||
proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
|
proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
|
||||||
rtl, extern: "nsuReplaceWord".} =
|
rtl, extern: "nsuReplaceWord".} =
|
||||||
## Replaces `sub` in `s` by the string `by`. Each occurance of `sub`
|
## Replaces `sub` in `s` by the string `by`.
|
||||||
## has to be surrounded by word boundaries (comparable to ``\\w`` in
|
##
|
||||||
## regular expressions), otherwise it is not replaced.
|
## Each occurance of `sub` has to be surrounded by word boundaries
|
||||||
|
## (comparable to ``\\w`` in regular expressions), otherwise it is not
|
||||||
|
## replaced.
|
||||||
const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
|
const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
|
||||||
var a {.noinit.}: TSkipTable
|
var a {.noinit.}: TSkipTable
|
||||||
result = ""
|
result = ""
|
||||||
|
|
@ -822,8 +883,9 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
|
||||||
|
|
||||||
proc delete*(s: var string, first, last: int) {.noSideEffect,
|
proc delete*(s: var string, first, last: int) {.noSideEffect,
|
||||||
rtl, extern: "nsuDelete".} =
|
rtl, extern: "nsuDelete".} =
|
||||||
## Deletes in `s` the characters at position `first` .. `last`. This modifies
|
## Deletes in `s` the characters at position `first` .. `last`.
|
||||||
## `s` itself, it does not return a copy.
|
##
|
||||||
|
## This modifies `s` itself, it does not return a copy.
|
||||||
var i = first
|
var i = first
|
||||||
var j = last+1
|
var j = last+1
|
||||||
var newLen = len(s)-j+i
|
var newLen = len(s)-j+i
|
||||||
|
|
@ -835,10 +897,11 @@ proc delete*(s: var string, first, last: int) {.noSideEffect,
|
||||||
|
|
||||||
proc parseOctInt*(s: string): int {.noSideEffect,
|
proc parseOctInt*(s: string): int {.noSideEffect,
|
||||||
rtl, extern: "nsuParseOctInt".} =
|
rtl, extern: "nsuParseOctInt".} =
|
||||||
## Parses an octal integer value contained in `s`. If `s` is not
|
## Parses an octal integer value contained in `s`.
|
||||||
## a valid integer, `EInvalidValue` is raised. `s` can have one of the
|
##
|
||||||
## following optional prefixes: ``0o``, ``0O``.
|
## If `s` is not a valid integer, `EInvalidValue` is raised. `s` can have one
|
||||||
## Underscores within `s` are ignored.
|
## of the following optional prefixes: ``0o``, ``0O``. Underscores within
|
||||||
|
## `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 s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2)
|
||||||
while true:
|
while true:
|
||||||
|
|
@ -852,8 +915,10 @@ proc parseOctInt*(s: string): int {.noSideEffect,
|
||||||
|
|
||||||
proc toOct*(x: BiggestInt, len: int): string {.noSideEffect,
|
proc toOct*(x: BiggestInt, len: int): string {.noSideEffect,
|
||||||
rtl, extern: "nsuToOct".} =
|
rtl, extern: "nsuToOct".} =
|
||||||
## converts `x` into its octal representation. The resulting string is
|
## Converts `x` into its octal representation.
|
||||||
## always `len` characters long. No leading ``0o`` prefix is generated.
|
##
|
||||||
|
## The resulting string is always `len` characters long. No leading ``0o``
|
||||||
|
## prefix is generated.
|
||||||
var
|
var
|
||||||
mask: BiggestInt = 7
|
mask: BiggestInt = 7
|
||||||
shift: BiggestInt = 0
|
shift: BiggestInt = 0
|
||||||
|
|
@ -866,8 +931,10 @@ proc toOct*(x: BiggestInt, len: int): string {.noSideEffect,
|
||||||
|
|
||||||
proc toBin*(x: BiggestInt, len: int): string {.noSideEffect,
|
proc toBin*(x: BiggestInt, len: int): string {.noSideEffect,
|
||||||
rtl, extern: "nsuToBin".} =
|
rtl, extern: "nsuToBin".} =
|
||||||
## converts `x` into its binary representation. The resulting string is
|
## Converts `x` into its binary representation.
|
||||||
## always `len` characters long. No leading ``0b`` prefix is generated.
|
##
|
||||||
|
## The resulting string is always `len` characters long. No leading ``0b``
|
||||||
|
## prefix is generated.
|
||||||
var
|
var
|
||||||
mask: BiggestInt = 1
|
mask: BiggestInt = 1
|
||||||
shift: BiggestInt = 0
|
shift: BiggestInt = 0
|
||||||
|
|
@ -880,7 +947,8 @@ proc toBin*(x: BiggestInt, len: int): string {.noSideEffect,
|
||||||
|
|
||||||
proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
|
proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
|
||||||
rtl, extern: "nsuInsertSep".} =
|
rtl, extern: "nsuInsertSep".} =
|
||||||
## inserts the separator `sep` after `digits` digits from right to left.
|
## Inserts the separator `sep` after `digits` digits from right to left.
|
||||||
|
##
|
||||||
## Even though the algorithm works with any string `s`, it is only useful
|
## Even though the algorithm works with any string `s`, it is only useful
|
||||||
## if `s` contains a number.
|
## if `s` contains a number.
|
||||||
## Example: ``insertSep("1000000") == "1_000_000"``
|
## Example: ``insertSep("1000000") == "1_000_000"``
|
||||||
|
|
@ -899,7 +967,9 @@ proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
|
||||||
|
|
||||||
proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
||||||
rtl, extern: "nsuEscape".} =
|
rtl, extern: "nsuEscape".} =
|
||||||
## Escapes a string `s`. This does these operations (at the same time):
|
## Escapes a string `s`.
|
||||||
|
##
|
||||||
|
## This does these operations (at the same time):
|
||||||
## * replaces any ``\`` by ``\\``
|
## * replaces any ``\`` by ``\\``
|
||||||
## * replaces any ``'`` by ``\'``
|
## * replaces any ``'`` by ``\'``
|
||||||
## * replaces any ``"`` by ``\"``
|
## * replaces any ``"`` by ``\"``
|
||||||
|
|
@ -923,11 +993,13 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
||||||
|
|
||||||
proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
||||||
rtl, extern: "nsuUnescape".} =
|
rtl, extern: "nsuUnescape".} =
|
||||||
## Unescapes a string `s`. This complements ``escape`` as it performs the
|
## Unescapes a string `s`.
|
||||||
## opposite operations.
|
|
||||||
##
|
##
|
||||||
## If `s` does not begin with ``prefix`` and end with ``suffix`` a EInvalidValue
|
## This complements `escape <#escape>`_ as it performs the opposite
|
||||||
## exception will be raised.
|
## operations.
|
||||||
|
##
|
||||||
|
## If `s` does not begin with ``prefix`` and end with ``suffix`` a
|
||||||
|
## EInvalidValue exception will be raised.
|
||||||
result = newStringOfCap(s.len)
|
result = newStringOfCap(s.len)
|
||||||
var i = 0
|
var i = 0
|
||||||
if s[0 .. prefix.len-1] != prefix:
|
if s[0 .. prefix.len-1] != prefix:
|
||||||
|
|
@ -961,9 +1033,10 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
||||||
|
|
||||||
proc validIdentifier*(s: string): bool {.noSideEffect,
|
proc validIdentifier*(s: string): bool {.noSideEffect,
|
||||||
rtl, extern: "nsuValidIdentifier".} =
|
rtl, extern: "nsuValidIdentifier".} =
|
||||||
## returns true if `s` is a valid identifier. A valid identifier starts
|
## Returns true if `s` is a valid identifier.
|
||||||
## with a character of the set `IdentStartChars` and is followed by any
|
##
|
||||||
## number of characters of the set `IdentChars`.
|
## A valid identifier starts with a character of the set `IdentStartChars`
|
||||||
|
## and is followed by any number of characters of the set `IdentChars`.
|
||||||
if s[0] in IdentStartChars:
|
if 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
|
||||||
|
|
@ -971,9 +1044,10 @@ proc validIdentifier*(s: string): bool {.noSideEffect,
|
||||||
|
|
||||||
proc editDistance*(a, b: string): int {.noSideEffect,
|
proc editDistance*(a, b: string): int {.noSideEffect,
|
||||||
rtl, extern: "nsuEditDistance".} =
|
rtl, extern: "nsuEditDistance".} =
|
||||||
## returns the edit distance between `a` and `b`. This uses the
|
## Returns the edit distance between `a` and `b`.
|
||||||
## `Levenshtein`:idx: distance algorithm with only a linear memory overhead.
|
##
|
||||||
## This implementation is highly optimized!
|
## This uses the `Levenshtein`:idx: distance algorithm with only a linear
|
||||||
|
## memory overhead. This implementation is highly optimized!
|
||||||
var len1 = a.len
|
var len1 = a.len
|
||||||
var len2 = b.len
|
var len2 = b.len
|
||||||
if len1 > len2:
|
if len1 > len2:
|
||||||
|
|
@ -1072,7 +1146,7 @@ type
|
||||||
proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault,
|
proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault,
|
||||||
precision: range[0..32] = 16): string {.
|
precision: range[0..32] = 16): string {.
|
||||||
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
|
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
|
||||||
## converts a floating point value `f` to a string.
|
## Converts a floating point value `f` to a string.
|
||||||
##
|
##
|
||||||
## If ``format == ffDecimal`` then precision is the number of digits to
|
## If ``format == ffDecimal`` then precision is the number of digits to
|
||||||
## be printed after the decimal point.
|
## be printed after the decimal point.
|
||||||
|
|
@ -1103,7 +1177,7 @@ proc formatBiggestFloat*(f: BiggestFloat, format: TFloatFormat = ffDefault,
|
||||||
proc formatFloat*(f: float, format: TFloatFormat = ffDefault,
|
proc formatFloat*(f: float, format: TFloatFormat = ffDefault,
|
||||||
precision: range[0..32] = 16): string {.
|
precision: range[0..32] = 16): string {.
|
||||||
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
|
noSideEffect, operator: 2, rtl, extern: "nsu$1".} =
|
||||||
## converts a floating point value `f` to a string.
|
## Converts a floating point value `f` to a string.
|
||||||
##
|
##
|
||||||
## If ``format == ffDecimal`` then precision is the number of digits to
|
## If ``format == ffDecimal`` then precision is the number of digits to
|
||||||
## be printed after the decimal point.
|
## be printed after the decimal point.
|
||||||
|
|
@ -1198,6 +1272,8 @@ proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
|
||||||
|
|
||||||
proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
|
proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
|
||||||
rtl, extern: "nsuFormatOpenArray".} =
|
rtl, extern: "nsuFormatOpenArray".} =
|
||||||
|
## Interpolates a format string with the values from `a`.
|
||||||
|
##
|
||||||
## The `substitution`:idx: operator performs string substitutions in
|
## The `substitution`:idx: operator performs string substitutions in
|
||||||
## `formatstr` and returns a modified `formatstr`. This is often called
|
## `formatstr` and returns a modified `formatstr`. This is often called
|
||||||
## `string interpolation`:idx:.
|
## `string interpolation`:idx:.
|
||||||
|
|
|
||||||
350
lib/pure/uri.nim
350
lib/pure/uri.nim
|
|
@ -7,17 +7,24 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## **Note**: This module will be deprecated in the future and merged into a
|
## This module implements URI parsing as specified by RFC 3986.
|
||||||
## new ``url`` module.
|
|
||||||
|
|
||||||
import strutils
|
import strutils, parseutils
|
||||||
type
|
type
|
||||||
TUrl* = distinct string
|
TUrl* = distinct string
|
||||||
|
|
||||||
proc `$`*(url: TUrl): string = return string(url)
|
TUri* = object
|
||||||
|
scheme*, username*, password*: string
|
||||||
|
hostname*, port*, path*, query*, anchor*: string
|
||||||
|
|
||||||
proc `/`*(a, b: TUrl): TUrl =
|
proc `$`*(url: TUrl): string {.deprecated.} =
|
||||||
|
## **Deprecated since 0.9.6**: Use ``TUri`` instead.
|
||||||
|
return string(url)
|
||||||
|
|
||||||
|
proc `/`*(a, b: TUrl): TUrl {.deprecated.} =
|
||||||
## Joins two URLs together, separating them with / if needed.
|
## Joins two URLs together, separating them with / if needed.
|
||||||
|
##
|
||||||
|
## **Deprecated since 0.9.6**: Use ``TUri`` instead.
|
||||||
var urlS = $a
|
var urlS = $a
|
||||||
var bS = $b
|
var bS = $b
|
||||||
if urlS == "": return b
|
if urlS == "": return b
|
||||||
|
|
@ -29,9 +36,338 @@ proc `/`*(a, b: TUrl): TUrl =
|
||||||
urlS.add(bs)
|
urlS.add(bs)
|
||||||
result = TUrl(urlS)
|
result = TUrl(urlS)
|
||||||
|
|
||||||
proc add*(url: var TUrl, a: TUrl) =
|
proc add*(url: var TUrl, a: TUrl) {.deprecated.} =
|
||||||
## Appends url to url.
|
## Appends url to url.
|
||||||
|
##
|
||||||
|
## **Deprecated since 0.9.6**: Use ``TUri`` instead.
|
||||||
url = url / a
|
url = url / a
|
||||||
|
|
||||||
|
proc parseAuthority(authority: string, result: var TUri) =
|
||||||
|
var i = 0
|
||||||
|
var inPort = false
|
||||||
|
while true:
|
||||||
|
case authority[i]
|
||||||
|
of '@':
|
||||||
|
result.password = result.port
|
||||||
|
result.port = ""
|
||||||
|
result.username = result.hostname
|
||||||
|
result.hostname = ""
|
||||||
|
inPort = false
|
||||||
|
of ':':
|
||||||
|
inPort = true
|
||||||
|
of '\0': break
|
||||||
|
else:
|
||||||
|
if inPort:
|
||||||
|
result.port.add(authority[i])
|
||||||
|
else:
|
||||||
|
result.hostname.add(authority[i])
|
||||||
|
i.inc
|
||||||
|
|
||||||
|
proc parsePath(uri: string, i: var int, result: var TUri) =
|
||||||
|
|
||||||
|
i.inc parseUntil(uri, result.path, {'?', '#'}, i)
|
||||||
|
|
||||||
|
# The 'mailto' scheme's PATH actually contains the hostname/username
|
||||||
|
if result.scheme.ToLower() == "mailto":
|
||||||
|
parseAuthority(result.path, result)
|
||||||
|
result.path = ""
|
||||||
|
|
||||||
|
if uri[i] == '?':
|
||||||
|
i.inc # Skip '?'
|
||||||
|
i.inc parseUntil(uri, result.query, {'#'}, i)
|
||||||
|
|
||||||
|
if uri[i] == '#':
|
||||||
|
i.inc # Skip '#'
|
||||||
|
i.inc parseUntil(uri, result.anchor, {}, i)
|
||||||
|
|
||||||
|
proc initUri(): TUri =
|
||||||
|
result = TUri(scheme: "", username: "", password: "", hostname: "", port: "",
|
||||||
|
path: "", query: "", anchor: "")
|
||||||
|
|
||||||
|
proc parseUri*(uri: string): TUri =
|
||||||
|
## Parses a URI.
|
||||||
|
result = initUri()
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
|
||||||
|
# Check if this is a reference URI (relative URI)
|
||||||
|
if uri[i] == '/':
|
||||||
|
parsePath(uri, i, result)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Scheme
|
||||||
|
i.inc parseWhile(uri, result.scheme, Letters + Digits + {'+', '-', '.'}, i)
|
||||||
|
if uri[i] != ':':
|
||||||
|
# Assume this is a reference URI (relative URI)
|
||||||
|
i = 0
|
||||||
|
result.scheme = ""
|
||||||
|
parsePath(uri, i, result)
|
||||||
|
return
|
||||||
|
i.inc # Skip ':'
|
||||||
|
|
||||||
|
# Authority
|
||||||
|
if uri[i] == '/' and uri[i+1] == '/':
|
||||||
|
i.inc(2) # Skip //
|
||||||
|
var authority = ""
|
||||||
|
i.inc parseUntil(uri, authority, {'/', '?', '#'}, i)
|
||||||
|
if authority == "":
|
||||||
|
raise newException(EInvalidValue, "Expected authority got nothing.")
|
||||||
|
parseAuthority(authority, result)
|
||||||
|
|
||||||
|
# Path
|
||||||
|
parsePath(uri, i, result)
|
||||||
|
|
||||||
|
proc removeDotSegments(path: string): string =
|
||||||
|
var collection: seq[string] = @[]
|
||||||
|
let endsWithSlash = path[path.len-1] == '/'
|
||||||
|
var i = 0
|
||||||
|
var currentSegment = ""
|
||||||
|
while true:
|
||||||
|
case path[i]
|
||||||
|
of '/':
|
||||||
|
collection.add(currentSegment)
|
||||||
|
currentSegment = ""
|
||||||
|
of '.':
|
||||||
|
if path[i+1] == '.' and path[i+2] == '/':
|
||||||
|
if collection.len > 0:
|
||||||
|
discard collection.pop()
|
||||||
|
i.inc 3
|
||||||
|
continue
|
||||||
|
elif path[i+1] == '/':
|
||||||
|
i.inc 2
|
||||||
|
continue
|
||||||
|
currentSegment.add path[i]
|
||||||
|
of '\0':
|
||||||
|
if currentSegment != "":
|
||||||
|
collection.add currentSegment
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
currentSegment.add path[i]
|
||||||
|
i.inc
|
||||||
|
|
||||||
|
result = collection.join("/")
|
||||||
|
if endsWithSlash: result.add '/'
|
||||||
|
|
||||||
|
proc merge(base, reference: TUri): string =
|
||||||
|
# http://tools.ietf.org/html/rfc3986#section-5.2.3
|
||||||
|
if base.hostname != "" and base.path == "":
|
||||||
|
'/' & reference.path
|
||||||
|
else:
|
||||||
|
let lastSegment = rfind(base.path, "/")
|
||||||
|
if lastSegment == -1:
|
||||||
|
reference.path
|
||||||
|
else:
|
||||||
|
base.path[0 .. lastSegment] & reference.path
|
||||||
|
|
||||||
|
proc combine*(base: TUri, reference: TUri): TUri =
|
||||||
|
## Combines a base URI with a reference URI.
|
||||||
|
##
|
||||||
|
## This uses the algorithm specified in
|
||||||
|
## `section 5.2.2 of RFC 3986 <http://tools.ietf.org/html/rfc3986#section-5.2.2>`_.
|
||||||
|
##
|
||||||
|
## This means that the slashes inside the base URI's path as well as reference
|
||||||
|
## URI's path affect the resulting URI.
|
||||||
|
##
|
||||||
|
## For building URIs you may wish to use \`/\` instead.
|
||||||
|
##
|
||||||
|
## Examples:
|
||||||
|
##
|
||||||
|
## .. code-block:: nimrod
|
||||||
|
## let foo = combine(parseUri("http://example.com/foo/bar"), parseUri("/baz"))
|
||||||
|
## assert foo.path == "/baz"
|
||||||
|
##
|
||||||
|
## let bar = combine(parseUri("http://example.com/foo/bar"), parseUri("baz"))
|
||||||
|
## assert foo.path == "/foo/baz"
|
||||||
|
##
|
||||||
|
## let bar = combine(parseUri("http://example.com/foo/bar/"), parseUri("baz"))
|
||||||
|
## assert foo.path == "/foo/bar/baz"
|
||||||
|
|
||||||
|
template setAuthority(dest, src: expr): stmt =
|
||||||
|
dest.hostname = src.hostname
|
||||||
|
dest.username = src.username
|
||||||
|
dest.port = src.port
|
||||||
|
dest.password = src.password
|
||||||
|
|
||||||
|
result = initUri()
|
||||||
|
if reference.scheme != base.scheme and reference.scheme != "":
|
||||||
|
result = reference
|
||||||
|
result.path = removeDotSegments(result.path)
|
||||||
|
else:
|
||||||
|
if reference.hostname != "":
|
||||||
|
setAuthority(result, reference)
|
||||||
|
result.path = removeDotSegments(reference.path)
|
||||||
|
result.query = reference.query
|
||||||
|
else:
|
||||||
|
if reference.path == "":
|
||||||
|
result.path = base.path
|
||||||
|
if reference.query != "":
|
||||||
|
result.query = reference.query
|
||||||
|
else:
|
||||||
|
result.query = base.query
|
||||||
|
else:
|
||||||
|
if reference.path.startsWith("/"):
|
||||||
|
result.path = removeDotSegments(reference.path)
|
||||||
|
else:
|
||||||
|
result.path = removeDotSegments(merge(base, reference))
|
||||||
|
result.query = reference.query
|
||||||
|
setAuthority(result, base)
|
||||||
|
result.scheme = base.scheme
|
||||||
|
result.anchor = reference.anchor
|
||||||
|
|
||||||
|
proc combine*(uris: varargs[TUri]): TUri =
|
||||||
|
## Combines multiple URIs together.
|
||||||
|
result = uris[0]
|
||||||
|
for i in 1 .. <uris.len:
|
||||||
|
result = combine(result, uris[i])
|
||||||
|
|
||||||
|
proc `/`*(x: TUri, path: string): TUri =
|
||||||
|
## Concatenates the path specified to the specified URI's path.
|
||||||
|
##
|
||||||
|
## Contrary to the ``combine`` procedure you do not have to worry about
|
||||||
|
## the slashes at the beginning and end of the path and URI's path
|
||||||
|
## respectively.
|
||||||
|
##
|
||||||
|
## Examples:
|
||||||
|
##
|
||||||
|
## .. code-block:: nimrod
|
||||||
|
## let foo = parseUri("http://example.com/foo/bar") / parseUri("/baz")
|
||||||
|
## assert foo.path == "/foo/bar/baz"
|
||||||
|
##
|
||||||
|
## let bar = parseUri("http://example.com/foo/bar") / parseUri("baz")
|
||||||
|
## assert foo.path == "/foo/bar/baz"
|
||||||
|
##
|
||||||
|
## let bar = parseUri("http://example.com/foo/bar/") / parseUri("baz")
|
||||||
|
## assert foo.path == "/foo/bar/baz"
|
||||||
|
result = x
|
||||||
|
if result.path[result.path.len-1] == '/':
|
||||||
|
if path[0] == '/':
|
||||||
|
result.path.add(path[1 .. path.len-1])
|
||||||
|
else:
|
||||||
|
result.path.add(path)
|
||||||
|
else:
|
||||||
|
if path[0] != '/':
|
||||||
|
result.path.add '/'
|
||||||
|
result.path.add(path)
|
||||||
|
|
||||||
|
proc `$`*(u: TUri): string =
|
||||||
|
## Returns the string representation of the specified URI object.
|
||||||
|
result = ""
|
||||||
|
if u.scheme.len > 0:
|
||||||
|
result.add(u.scheme)
|
||||||
|
result.add("://")
|
||||||
|
if u.username.len > 0:
|
||||||
|
result.add(u.username)
|
||||||
|
if u.password.len > 0:
|
||||||
|
result.add(":")
|
||||||
|
result.add(u.password)
|
||||||
|
result.add("@")
|
||||||
|
result.add(u.hostname)
|
||||||
|
if u.port.len > 0:
|
||||||
|
result.add(":")
|
||||||
|
result.add(u.port)
|
||||||
|
if u.path.len > 0:
|
||||||
|
if u.path[0] != '/': result.add("/")
|
||||||
|
result.add(u.path)
|
||||||
|
result.add(u.query)
|
||||||
|
result.add(u.anchor)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
assert($("http://".TUrl / "localhost:5000".TUrl) == "http://localhost:5000")
|
block:
|
||||||
|
let test = parseUri("http://localhost:8080/test")
|
||||||
|
doAssert test.scheme == "http"
|
||||||
|
doAssert test.port == "8080"
|
||||||
|
doAssert test.path == "/test"
|
||||||
|
doAssert test.hostname == "localhost"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let test = parseUri("foo://username:password@example.com:8042/over/there" &
|
||||||
|
"/index.dtb?type=animal&name=narwhal#nose")
|
||||||
|
doAssert test.scheme == "foo"
|
||||||
|
doAssert test.username == "username"
|
||||||
|
doAssert test.password == "password"
|
||||||
|
doAssert test.hostname == "example.com"
|
||||||
|
doAssert test.port == "8042"
|
||||||
|
doAssert test.path == "/over/there/index.dtb"
|
||||||
|
doAssert test.query == "type=animal&name=narwhal"
|
||||||
|
doAssert test.anchor == "nose"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let test = parseUri("urn:example:animal:ferret:nose")
|
||||||
|
doAssert test.scheme == "urn"
|
||||||
|
doAssert test.path == "example:animal:ferret:nose"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let test = parseUri("mailto:username@example.com?subject=Topic")
|
||||||
|
doAssert test.scheme == "mailto"
|
||||||
|
doAssert test.username == "username"
|
||||||
|
doAssert test.hostname == "example.com"
|
||||||
|
doAssert test.query == "subject=Topic"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let test = parseUri("magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar")
|
||||||
|
doAssert test.scheme == "magnet"
|
||||||
|
doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let test = parseUri("/test/foo/bar?q=2#asdf")
|
||||||
|
doAssert test.scheme == ""
|
||||||
|
doAssert test.path == "/test/foo/bar"
|
||||||
|
doAssert test.query == "q=2"
|
||||||
|
doAssert test.anchor == "asdf"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let test = parseUri("test/no/slash")
|
||||||
|
doAssert test.path == "test/no/slash"
|
||||||
|
|
||||||
|
# Remove dot segments tests
|
||||||
|
block:
|
||||||
|
doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz"
|
||||||
|
|
||||||
|
# Combine tests
|
||||||
|
block:
|
||||||
|
let concat = combine(parseUri("http://google.com/foo/bar/"), parseUri("baz"))
|
||||||
|
doAssert concat.path == "/foo/bar/baz"
|
||||||
|
doAssert concat.hostname == "google.com"
|
||||||
|
doAssert concat.scheme == "http"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let concat = combine(parseUri("http://google.com/foo"), parseUri("/baz"))
|
||||||
|
doAssert concat.path == "/baz"
|
||||||
|
doAssert concat.hostname == "google.com"
|
||||||
|
doAssert concat.scheme == "http"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar"))
|
||||||
|
doAssert concat.path == "/foo/bar"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let concat = combine(parseUri("http://google.com/foo/test"), parseUri("/bar"))
|
||||||
|
doAssert concat.path == "/bar"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar"))
|
||||||
|
doAssert concat.path == "/foo/bar"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar"))
|
||||||
|
doAssert concat.path == "/foo/test/bar"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/"))
|
||||||
|
doAssert concat.path == "/foo/test/bar/"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/"),
|
||||||
|
parseUri("baz"))
|
||||||
|
doAssert concat.path == "/foo/test/bar/baz"
|
||||||
|
|
||||||
|
# `/` tests
|
||||||
|
block:
|
||||||
|
let test = parseUri("http://example.com/foo") / "bar/asd"
|
||||||
|
doAssert test.path == "/foo/bar/asd"
|
||||||
|
|
||||||
|
block:
|
||||||
|
let test = parseUri("http://example.com/foo/") / "/bar/asd"
|
||||||
|
doAssert test.path == "/foo/bar/asd"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -783,7 +783,7 @@ proc contains*[T](s: TSlice[T], value: T): bool {.noSideEffect, inline.} =
|
||||||
result = s.a <= value and value <= s.b
|
result = s.a <= value and value <= s.b
|
||||||
|
|
||||||
template `in` * (x, y: expr): expr {.immediate.} = contains(y, x)
|
template `in` * (x, y: expr): expr {.immediate.} = contains(y, x)
|
||||||
## Suger for contains
|
## Sugar for contains
|
||||||
##
|
##
|
||||||
## .. code-block:: Nimrod
|
## .. code-block:: Nimrod
|
||||||
## assert(1 in (1..3) == true)
|
## assert(1 in (1..3) == true)
|
||||||
|
|
@ -1052,7 +1052,7 @@ proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
|
||||||
## containers should also call their adding proc `add` for consistency.
|
## containers should also call their adding proc `add` for consistency.
|
||||||
## Generic code becomes much easier to write if the Nimrod naming scheme is
|
## Generic code becomes much easier to write if the Nimrod naming scheme is
|
||||||
## respected.
|
## respected.
|
||||||
var xl = x.len
|
let xl = x.len
|
||||||
setLen(x, xl + y.len)
|
setLen(x, xl + y.len)
|
||||||
for i in 0..high(y): x[xl+i] = y[i]
|
for i in 0..high(y): x[xl+i] = y[i]
|
||||||
|
|
||||||
|
|
@ -1066,20 +1066,20 @@ proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".}
|
||||||
proc del*[T](x: var seq[T], i: int) {.noSideEffect.} =
|
proc del*[T](x: var seq[T], i: int) {.noSideEffect.} =
|
||||||
## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`.
|
## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`.
|
||||||
## This is an O(1) operation.
|
## This is an O(1) operation.
|
||||||
var xl = x.len
|
let xl = x.len
|
||||||
shallowCopy(x[i], x[xl-1])
|
shallowCopy(x[i], x[xl-1])
|
||||||
setLen(x, xl-1)
|
setLen(x, xl-1)
|
||||||
|
|
||||||
proc delete*[T](x: var seq[T], i: int) {.noSideEffect.} =
|
proc delete*[T](x: var seq[T], i: int) {.noSideEffect.} =
|
||||||
## deletes the item at index `i` by moving ``x[i+1..]`` by one position.
|
## deletes the item at index `i` by moving ``x[i+1..]`` by one position.
|
||||||
## This is an O(n) operation.
|
## This is an O(n) operation.
|
||||||
var xl = x.len
|
let xl = x.len
|
||||||
for j in i..xl-2: shallowCopy(x[j], x[j+1])
|
for j in i..xl-2: shallowCopy(x[j], x[j+1])
|
||||||
setLen(x, xl-1)
|
setLen(x, xl-1)
|
||||||
|
|
||||||
proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} =
|
proc insert*[T](x: var seq[T], item: T, i = 0) {.noSideEffect.} =
|
||||||
## inserts `item` into `x` at position `i`.
|
## inserts `item` into `x` at position `i`.
|
||||||
var xl = x.len
|
let xl = x.len
|
||||||
setLen(x, xl+1)
|
setLen(x, xl+1)
|
||||||
var j = xl-1
|
var j = xl-1
|
||||||
while j >= i:
|
while j >= i:
|
||||||
|
|
@ -2026,19 +2026,25 @@ elif hostOS != "standalone":
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo", tags: [FWriteIO], gcsafe.}
|
proc echo*[T](x: varargs[T, `$`]) {.magic: "Echo", tags: [FWriteIO], gcsafe.}
|
||||||
## special built-in that takes a variable number of arguments. Each argument
|
## Writes and flushes the parameters to the standard output.
|
||||||
|
##
|
||||||
|
## Special built-in that takes a variable number of arguments. Each argument
|
||||||
## is converted to a string via ``$``, so it works for user-defined
|
## is converted to a string via ``$``, so it works for user-defined
|
||||||
## types that have an overloaded ``$`` operator.
|
## types that have an overloaded ``$`` operator.
|
||||||
## It is roughly equivalent to ``writeln(stdout, x); flush(stdout)``, but
|
## It is roughly equivalent to ``writeln(stdout, x); flush(stdout)``, but
|
||||||
## available for the JavaScript target too.
|
## available for the JavaScript target too.
|
||||||
|
##
|
||||||
## Unlike other IO operations this is guaranteed to be thread-safe as
|
## Unlike other IO operations this is guaranteed to be thread-safe as
|
||||||
## ``echo`` is very often used for debugging convenience.
|
## ``echo`` is very often used for debugging convenience. If you want to use
|
||||||
|
## ``echo`` inside a `proc without side effects
|
||||||
|
## <manual.html#nosideeffect-pragma>`_ you can use `debugEcho <#debugEcho>`_
|
||||||
|
## instead.
|
||||||
|
|
||||||
proc debugEcho*[T](x: varargs[T, `$`]) {.magic: "Echo", noSideEffect,
|
proc debugEcho*[T](x: varargs[T, `$`]) {.magic: "Echo", noSideEffect,
|
||||||
tags: [], raises: [].}
|
tags: [], raises: [].}
|
||||||
## Same as ``echo``, but as a special semantic rule, ``debugEcho`` pretends
|
## Same as `echo <#echo>`_, but as a special semantic rule, ``debugEcho``
|
||||||
## to be free of side effects, so that it can be used for debugging routines
|
## pretends to be free of side effects, so that it can be used for debugging
|
||||||
## marked as ``noSideEffect``.
|
## routines marked as `noSideEffect <manual.html#nosideeffect-pragma>`_.
|
||||||
|
|
||||||
template newException*(exceptn: typedesc, message: string): expr =
|
template newException*(exceptn: typedesc, message: string): expr =
|
||||||
## creates an exception object of type ``exceptn`` and sets its ``msg`` field
|
## creates an exception object of type ``exceptn`` and sets its ``msg`` field
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
output: ''''''
|
output: ''''''
|
||||||
"""
|
"""
|
||||||
import unittest
|
|
||||||
import sets
|
import sets
|
||||||
|
|
||||||
doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3,4]), "equivalent or subset")
|
doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3,4]), "equivalent or subset")
|
||||||
|
|
|
||||||
|
|
@ -101,5 +101,5 @@ Roadmap to 1.0
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Please have a look at
|
Please have a look at
|
||||||
this `wiki page <https://github.com/Araq/Nimrod/wiki/Feature-Matrix>`_ for
|
this `wiki page <https://github.com/Araq/Nimrod/wiki/Roadmap>`_ for
|
||||||
an up-to-date overview.
|
an up-to-date overview.
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ News
|
||||||
- ``pas2nim`` moved into its own repository and is now a Babel package.
|
- ``pas2nim`` moved into its own repository and is now a Babel package.
|
||||||
- ``system.$`` for floating point types now produces a human friendly string
|
- ``system.$`` for floating point types now produces a human friendly string
|
||||||
representation.
|
representation.
|
||||||
|
- ``uri.TUrl`` as well as the ``parseurl`` module are now deprecated in favour
|
||||||
|
of the new ``TUri`` type in the ``uri`` module.
|
||||||
|
|
||||||
Library Additions
|
Library Additions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
@ -26,7 +28,7 @@ News
|
||||||
- Added module ``threadpool``.
|
- Added module ``threadpool``.
|
||||||
- ``sequtils.distnct`` has been renamed to ``sequtils.deduplicate``.
|
- ``sequtils.distnct`` has been renamed to ``sequtils.deduplicate``.
|
||||||
- Added ``algorithm.reversed``
|
- Added ``algorithm.reversed``
|
||||||
|
- Added ``uri.combine`` and ``uri.parseUri``.
|
||||||
|
|
||||||
2014-04-21 Version 0.9.4 released
|
2014-04-21 Version 0.9.4 released
|
||||||
=================================
|
=================================
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ UNIX. We don't believe this to be a coincidence. - Jeremy S. Anderson."""
|
||||||
|
|
||||||
[Documentation]
|
[Documentation]
|
||||||
doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview;filters"
|
doc: "endb;intern;apis;lib;manual;tut1;tut2;nimrodc;overview;filters"
|
||||||
doc: "tools;c2nim;niminst;nimgrep;gc;estp;idetools;docgen;koch;backends.txt"
|
doc: "tools;niminst;nimgrep;gc;estp;idetools;docgen;koch;backends.txt"
|
||||||
pdf: "manual;lib;tut1;tut2;nimrodc;niminst;gc"
|
pdf: "manual;lib;tut1;tut2;nimrodc;niminst;gc"
|
||||||
srcdoc2: "system.nim;impure/graphics;wrappers/sdl"
|
srcdoc2: "system.nim;impure/graphics;wrappers/sdl"
|
||||||
srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned"
|
srcdoc2: "core/macros;pure/marshal;core/typeinfo;core/unsigned"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue