better docs: strtabs (#10704)
This commit is contained in:
parent
8cf97908cc
commit
51f516e030
2 changed files with 213 additions and 109 deletions
|
|
@ -137,8 +137,7 @@ String handling
|
||||||
* `strtabs <strtabs.html>`_
|
* `strtabs <strtabs.html>`_
|
||||||
The ``strtabs`` module implements an efficient hash table that is a mapping
|
The ``strtabs`` module implements an efficient hash table that is a mapping
|
||||||
from strings to strings. Supports a case-sensitive, case-insensitive and
|
from strings to strings. Supports a case-sensitive, case-insensitive and
|
||||||
style-insensitive mode. An efficient string substitution operator ``%``
|
style-insensitive modes.
|
||||||
for the string table is also provided.
|
|
||||||
|
|
||||||
* `unicode <unicode.html>`_
|
* `unicode <unicode.html>`_
|
||||||
This module provides support to handle the Unicode UTF-8 encoding.
|
This module provides support to handle the Unicode UTF-8 encoding.
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,9 @@
|
||||||
|
|
||||||
## The ``strtabs`` module implements an efficient hash table that is a mapping
|
## The ``strtabs`` module implements an efficient hash table that is a mapping
|
||||||
## from strings to strings. Supports a case-sensitive, case-insensitive and
|
## from strings to strings. Supports a case-sensitive, case-insensitive and
|
||||||
## style-insensitive mode. An efficient string substitution operator ``%``
|
## style-insensitive mode.
|
||||||
## for the string table is also provided.
|
|
||||||
|
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
|
|
||||||
var t = newStringTable()
|
var t = newStringTable()
|
||||||
t["name"] = "John"
|
t["name"] = "John"
|
||||||
t["city"] = "Monaco"
|
t["city"] = "Monaco"
|
||||||
|
|
@ -22,17 +20,13 @@ runnableExamples:
|
||||||
doAssert "name" in t
|
doAssert "name" in t
|
||||||
|
|
||||||
## String tables can be created from a table constructor:
|
## String tables can be created from a table constructor:
|
||||||
|
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
||||||
|
|
||||||
|
## When using the style insensitive mode (``modeStyleInsensitive``),
|
||||||
## When using the style insensitive mode ``modeStyleInsensitive``,
|
|
||||||
## all letters are compared case insensitively within the ASCII range
|
## all letters are compared case insensitively within the ASCII range
|
||||||
## and underscores are ignored.
|
## and underscores are ignored.
|
||||||
|
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
|
|
||||||
var x = newStringTable(modeStyleInsensitive)
|
var x = newStringTable(modeStyleInsensitive)
|
||||||
x["first_name"] = "John"
|
x["first_name"] = "John"
|
||||||
x["LastName"] = "Doe"
|
x["LastName"] = "Doe"
|
||||||
|
|
@ -40,6 +34,21 @@ runnableExamples:
|
||||||
doAssert x["firstName"] == "John"
|
doAssert x["firstName"] == "John"
|
||||||
doAssert x["last_name"] == "Doe"
|
doAssert x["last_name"] == "Doe"
|
||||||
|
|
||||||
|
## An efficient string substitution operator
|
||||||
|
## `% <#%25,string,StringTableRef,set[FormatFlag]>`_ for the string table
|
||||||
|
## is also provided.
|
||||||
|
runnableExamples:
|
||||||
|
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
||||||
|
doAssert "${name} lives in ${city}" % t == "John lives in Monaco"
|
||||||
|
|
||||||
|
## **See also:**
|
||||||
|
## * `tables module <tables.html>`_ for general hash tables
|
||||||
|
## * `sharedtables module<sharedtables.html>`_ for shared hash table support
|
||||||
|
## * `strutils module<strutils.html>`_ for common string functions
|
||||||
|
## * `json module<json.html>`_ for table-like structure which allows
|
||||||
|
## heterogeneous members
|
||||||
|
|
||||||
|
|
||||||
import
|
import
|
||||||
hashes, strutils
|
hashes, strutils
|
||||||
|
|
||||||
|
|
@ -51,10 +60,10 @@ else:
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
||||||
type
|
type
|
||||||
StringTableMode* = enum ## describes the tables operation mode
|
StringTableMode* = enum ## Describes the tables operation mode.
|
||||||
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, hasValue: bool]
|
KeyValuePair = tuple[key, val: string, hasValue: bool]
|
||||||
KeyValuePairSeq = seq[KeyValuePair]
|
KeyValuePairSeq = seq[KeyValuePair]
|
||||||
StringTableObj* = object of RootObj
|
StringTableObj* = object of RootObj
|
||||||
|
|
@ -62,43 +71,41 @@ type
|
||||||
data: KeyValuePairSeq
|
data: KeyValuePairSeq
|
||||||
mode: StringTableMode
|
mode: StringTableMode
|
||||||
|
|
||||||
StringTableRef* = ref StringTableObj ## use this type to declare string tables
|
StringTableRef* = ref StringTableObj
|
||||||
|
|
||||||
|
FormatFlag* = enum ## Flags for the `%` operator.
|
||||||
|
useEnvironment, ## Use environment variable if the ``$key``
|
||||||
|
## is not found in the table.
|
||||||
|
## Does nothing when using `js` target.
|
||||||
|
useEmpty, ## Use the empty string as a default, thus it
|
||||||
|
## won't throw an exception if ``$key`` is not
|
||||||
|
## in the table.
|
||||||
|
useKey ## Do not replace ``$key`` if it is not found
|
||||||
|
## in the table (or in the environment).
|
||||||
|
|
||||||
|
const
|
||||||
|
growthFactor = 2
|
||||||
|
startSize = 64
|
||||||
|
|
||||||
proc len*(t: StringTableRef): int {.rtlFunc, extern: "nst$1".} =
|
|
||||||
## returns the number of keys in `t`.
|
|
||||||
result = t.counter
|
|
||||||
|
|
||||||
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 t.data[h].hasValue:
|
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 t.data[h].hasValue:
|
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 t.data[h].hasValue:
|
if t.data[h].hasValue:
|
||||||
yield t.data[h].val
|
yield t.data[h].val
|
||||||
|
|
||||||
type
|
|
||||||
FormatFlag* = enum ## flags for the `%` operator
|
|
||||||
useEnvironment, ## use environment variable if the ``$key``
|
|
||||||
## is not found in the table. Does nothing when using `js` target.
|
|
||||||
useEmpty, ## use the empty string as a default, thus it
|
|
||||||
## won't throw an exception if ``$key`` is not
|
|
||||||
## in the table
|
|
||||||
useKey ## do not replace ``$key`` if it is not found
|
|
||||||
## in the table (or in the environment)
|
|
||||||
|
|
||||||
const
|
|
||||||
growthFactor = 2
|
|
||||||
startSize = 64
|
|
||||||
|
|
||||||
proc myhash(t: StringTableRef, key: string): Hash =
|
proc myhash(t: StringTableRef, key: string): Hash =
|
||||||
case t.mode
|
case t.mode
|
||||||
|
|
@ -136,24 +143,118 @@ template get(t: StringTableRef, key: string) =
|
||||||
else:
|
else:
|
||||||
raise newException(KeyError, "key not found")
|
raise newException(KeyError, "key not found")
|
||||||
|
|
||||||
|
proc `[]=`*(t: StringTableRef, key, val: string) {.
|
||||||
|
rtlFunc, extern: "nstPut", noSideEffect.}
|
||||||
|
|
||||||
|
proc newStringTable*(mode: StringTableMode): StringTableRef {.
|
||||||
|
rtlFunc, extern: "nst$1".} =
|
||||||
|
## Creates a new empty string table.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `newStringTable(keyValuePairs) proc
|
||||||
|
## <#newStringTable,varargs[tuple[string,string]],StringTableMode>`_
|
||||||
|
new(result)
|
||||||
|
result.mode = mode
|
||||||
|
result.counter = 0
|
||||||
|
newSeq(result.data, startSize)
|
||||||
|
|
||||||
|
proc newStringTable*(keyValuePairs: varargs[string],
|
||||||
|
mode: StringTableMode): StringTableRef {.
|
||||||
|
rtlFunc, extern: "nst$1WithPairs".} =
|
||||||
|
## Creates a new string table with given `key, value` string pairs.
|
||||||
|
##
|
||||||
|
## `StringTableMode` must be specified.
|
||||||
|
runnableExamples:
|
||||||
|
var mytab = newStringTable("key1", "val1", "key2", "val2",
|
||||||
|
modeCaseInsensitive)
|
||||||
|
|
||||||
|
result = newStringTable(mode)
|
||||||
|
var i = 0
|
||||||
|
while i < high(keyValuePairs):
|
||||||
|
result[keyValuePairs[i]] = keyValuePairs[i + 1]
|
||||||
|
inc(i, 2)
|
||||||
|
|
||||||
|
proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]],
|
||||||
|
mode: StringTableMode = modeCaseSensitive): StringTableRef {.
|
||||||
|
rtlFunc, extern: "nst$1WithTableConstr".} =
|
||||||
|
## Creates a new string table with given `(key, value)` tuple pairs.
|
||||||
|
##
|
||||||
|
## The default mode is case sensitive.
|
||||||
|
runnableExamples:
|
||||||
|
var
|
||||||
|
mytab1 = newStringTable({"key1": "val1", "key2": "val2"}, modeCaseInsensitive)
|
||||||
|
mytab2 = newStringTable([("key3", "val3"), ("key4", "val4")])
|
||||||
|
|
||||||
|
result = newStringTable(mode)
|
||||||
|
for key, val in items(keyValuePairs): result[key] = val
|
||||||
|
|
||||||
|
proc len*(t: StringTableRef): int {.rtlFunc, extern: "nst$1".} =
|
||||||
|
## Returns the number of keys in `t`.
|
||||||
|
result = t.counter
|
||||||
|
|
||||||
proc `[]`*(t: StringTableRef, key: string): var string {.
|
proc `[]`*(t: StringTableRef, key: string): var string {.
|
||||||
rtlFunc, extern: "nstTake".} =
|
rtlFunc, extern: "nstTake".} =
|
||||||
## retrieves the location at ``t[key]``. If `key` is not in `t`, the
|
## Retrieves the location at ``t[key]``.
|
||||||
## ``KeyError`` exception is raised. One can check with ``hasKey`` whether
|
##
|
||||||
## the key exists.
|
## If `key` is not in `t`, the ``KeyError`` exception is raised.
|
||||||
|
## One can check with `hasKey proc <#hasKey,StringTableRef,string>`_
|
||||||
|
## whether the key exists.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `getOrDefault proc <#getOrDefault,StringTableRef,string,string>`_
|
||||||
|
## * `[]= proc <#[]=,StringTableRef,string,string>`_ for inserting a new
|
||||||
|
## (key, value) pair in the table
|
||||||
|
## * `hasKey proc <#hasKey,StringTableRef,string>`_ for checking if a key
|
||||||
|
## is in the table
|
||||||
|
runnableExamples:
|
||||||
|
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
||||||
|
doAssert t["name"] == "John"
|
||||||
|
doAssertRaises(KeyError):
|
||||||
|
echo t["occupation"]
|
||||||
get(t, key)
|
get(t, key)
|
||||||
|
|
||||||
proc getOrDefault*(t: StringTableRef; key: string, default: string = ""): string =
|
proc getOrDefault*(t: StringTableRef; key: string, default: string = ""): string =
|
||||||
|
## Retrieves the location at ``t[key]``.
|
||||||
|
##
|
||||||
|
## If `key` is not in `t`, the default value is returned (if not specified,
|
||||||
|
## it is an empty string (`""`)).
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `[] proc <#[],StringTableRef,string>`_ for retrieving a value of a key
|
||||||
|
## * `hasKey proc <#hasKey,StringTableRef,string>`_ for checking if a key
|
||||||
|
## is in the table
|
||||||
|
## * `[]= proc <#[]=,StringTableRef,string,string>`_ for inserting a new
|
||||||
|
## (key, value) pair in the table
|
||||||
|
runnableExamples:
|
||||||
|
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
||||||
|
doAssert t.getOrDefault("name") == "John"
|
||||||
|
doAssert t.getOrDefault("occupation") == ""
|
||||||
|
doAssert t.getOrDefault("occupation", "teacher") == "teacher"
|
||||||
|
doAssert t.getOrDefault("name", "Paul") == "John"
|
||||||
|
|
||||||
var index = rawGet(t, key)
|
var index = rawGet(t, key)
|
||||||
if index >= 0: result = t.data[index].val
|
if index >= 0: result = t.data[index].val
|
||||||
else: result = default
|
else: result = default
|
||||||
|
|
||||||
proc hasKey*(t: StringTableRef, key: string): bool {.rtlFunc, extern: "nst$1".} =
|
proc hasKey*(t: StringTableRef, key: string): bool {.rtlFunc, extern: "nst$1".} =
|
||||||
## returns true iff `key` is in the table `t`.
|
## Returns true if `key` is in the table `t`.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `getOrDefault proc <#getOrDefault,StringTableRef,string,string>`_
|
||||||
|
## * `contains proc <#contains,StringTableRef,string>`_
|
||||||
|
runnableExamples:
|
||||||
|
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
||||||
|
doAssert t.hasKey("name")
|
||||||
|
doAssert not t.hasKey("occupation")
|
||||||
result = rawGet(t, key) >= 0
|
result = rawGet(t, key) >= 0
|
||||||
|
|
||||||
proc contains*(t: StringTableRef, key: string): bool =
|
proc contains*(t: StringTableRef, key: string): bool =
|
||||||
## alias of `hasKey` for use with the `in` operator.
|
## Alias of `hasKey proc <#hasKey,StringTableRef,string>`_ for use with
|
||||||
|
## the `in` operator.
|
||||||
|
runnableExamples:
|
||||||
|
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
||||||
|
doAssert "name" in t
|
||||||
|
doAssert "occupation" notin t
|
||||||
return hasKey(t, key)
|
return hasKey(t, key)
|
||||||
|
|
||||||
proc rawInsert(t: StringTableRef, data: var KeyValuePairSeq, key, val: string) =
|
proc rawInsert(t: StringTableRef, data: var KeyValuePairSeq, key, val: string) =
|
||||||
|
|
@ -171,8 +272,18 @@ proc enlarge(t: StringTableRef) =
|
||||||
if t.data[i].hasValue: 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) {.
|
||||||
## puts a (key, value)-pair into `t`.
|
rtlFunc, extern: "nstPut", noSideEffect.} =
|
||||||
|
## Inserts a `(key, value)` pair into `t`.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `[] proc <#[],StringTableRef,string>`_ for retrieving a value of a key
|
||||||
|
## * `del proc <#del,StringTableRef,string>`_ for removing a key from the table
|
||||||
|
runnableExamples:
|
||||||
|
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
||||||
|
t["occupation"] = "teacher"
|
||||||
|
doAssert t.hasKey("occupation")
|
||||||
|
|
||||||
var index = rawGet(t, key)
|
var index = rawGet(t, key)
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
t.data[index].val = val
|
t.data[index].val = val
|
||||||
|
|
@ -199,45 +310,76 @@ proc getValue(t: StringTableRef, flags: set[FormatFlag], key: string): string =
|
||||||
if useKey in flags: result = '$' & key
|
if useKey in flags: result = '$' & key
|
||||||
elif useEmpty notin flags: raiseFormatException(key)
|
elif useEmpty notin flags: raiseFormatException(key)
|
||||||
|
|
||||||
proc newStringTable*(mode: StringTableMode): StringTableRef {.
|
|
||||||
rtlFunc, extern: "nst$1".} =
|
|
||||||
## creates a new string table that is empty.
|
|
||||||
new(result)
|
|
||||||
result.mode = mode
|
|
||||||
result.counter = 0
|
|
||||||
newSeq(result.data, startSize)
|
|
||||||
|
|
||||||
proc clear*(s: StringTableRef, mode: StringTableMode) {.
|
proc clear*(s: StringTableRef, mode: StringTableMode) {.
|
||||||
rtlFunc, extern: "nst$1".} =
|
rtlFunc, extern: "nst$1".} =
|
||||||
## resets a string table to be empty again.
|
## Resets a string table to be empty again.
|
||||||
|
##
|
||||||
|
## See also:
|
||||||
|
## * `del proc <#del,StringTableRef,string>`_ for removing a key from the table
|
||||||
|
runnableExamples:
|
||||||
|
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
||||||
|
clear(t, modeCaseSensitive)
|
||||||
|
doAssert len(t) == 0
|
||||||
|
doAssert "name" notin t
|
||||||
|
doAssert "city" notin t
|
||||||
s.mode = mode
|
s.mode = mode
|
||||||
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:
|
||||||
s.data[i].hasValue = false
|
s.data[i].hasValue = false
|
||||||
|
|
||||||
proc newStringTable*(keyValuePairs: varargs[string],
|
proc del*(t: StringTableRef, key: string) =
|
||||||
mode: StringTableMode): StringTableRef {.
|
## Removes `key` from `t`.
|
||||||
rtlFunc, extern: "nst$1WithPairs".} =
|
##
|
||||||
## creates a new string table with given key value pairs.
|
## See also:
|
||||||
## Example::
|
## * `clear proc <#clear,StringTableRef,StringTableMode>`_ for reseting a
|
||||||
## var mytab = newStringTable("key1", "val1", "key2", "val2",
|
## table to be empty
|
||||||
## modeCaseInsensitive)
|
## * `[]= proc <#[]=,StringTableRef,string,string>`_ for inserting a new
|
||||||
result = newStringTable(mode)
|
## (key, value) pair in the table
|
||||||
var i = 0
|
runnableExamples:
|
||||||
while i < high(keyValuePairs):
|
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
||||||
result[keyValuePairs[i]] = keyValuePairs[i + 1]
|
t.del("name")
|
||||||
inc(i, 2)
|
doAssert len(t) == 1
|
||||||
|
doAssert "name" notin t
|
||||||
|
doAssert "city" in t
|
||||||
|
|
||||||
proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]],
|
# Impl adapted from `tableimpl.delImplIdx`
|
||||||
mode: StringTableMode = modeCaseSensitive): StringTableRef {.
|
var i = rawGet(t, key)
|
||||||
rtlFunc, extern: "nst$1WithTableConstr".} =
|
let msk = high(t.data)
|
||||||
## creates a new string table with given key value pairs.
|
if i >= 0:
|
||||||
## Example::
|
dec(t.counter)
|
||||||
## var mytab = newStringTable({"key1": "val1", "key2": "val2"},
|
block outer:
|
||||||
## modeCaseInsensitive)
|
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
|
||||||
result = newStringTable(mode)
|
var j = i # The correctness of this depends on (h+1) in nextTry,
|
||||||
for key, val in items(keyValuePairs): result[key] = val
|
var r = j # though may be adaptable to other simple sequences.
|
||||||
|
t.data[i].hasValue = false # mark current EMPTY
|
||||||
|
t.data[i].key = ""
|
||||||
|
t.data[i].val = ""
|
||||||
|
while true:
|
||||||
|
i = (i + 1) and msk # increment mod table size
|
||||||
|
if not t.data[i].hasValue: # end of collision cluster; So all done
|
||||||
|
break outer
|
||||||
|
r = t.myhash(t.data[i].key) and msk # "home" location of key@i
|
||||||
|
if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
|
||||||
|
break
|
||||||
|
when defined(js):
|
||||||
|
t.data[j] = t.data[i]
|
||||||
|
else:
|
||||||
|
shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop
|
||||||
|
|
||||||
|
proc `$`*(t: StringTableRef): string {.rtlFunc, extern: "nstDollar".} =
|
||||||
|
## The `$` operator for string tables. Used internally when calling
|
||||||
|
## `echo` on a table.
|
||||||
|
if t.len == 0:
|
||||||
|
result = "{:}"
|
||||||
|
else:
|
||||||
|
result = "{"
|
||||||
|
for key, val in pairs(t):
|
||||||
|
if result.len > 1: result.add(", ")
|
||||||
|
result.add(key)
|
||||||
|
result.add(": ")
|
||||||
|
result.add(val)
|
||||||
|
result.add("}")
|
||||||
|
|
||||||
proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {.
|
proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {.
|
||||||
rtlFunc, extern: "nstFormat".} =
|
rtlFunc, extern: "nstFormat".} =
|
||||||
|
|
@ -245,6 +387,7 @@ proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
var t = {"name": "John", "city": "Monaco"}.newStringTable
|
||||||
doAssert "${name} lives in ${city}" % t == "John lives in Monaco"
|
doAssert "${name} lives in ${city}" % t == "John lives in Monaco"
|
||||||
|
|
||||||
const
|
const
|
||||||
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
|
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'}
|
||||||
result = ""
|
result = ""
|
||||||
|
|
@ -272,44 +415,6 @@ proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {.
|
||||||
add(result, f[i])
|
add(result, f[i])
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
proc del*(t: StringTableRef, key: string) =
|
|
||||||
## Removes `key` from `t`.
|
|
||||||
# Impl adapted from `tableimpl.delImplIdx`
|
|
||||||
var i = rawGet(t, key)
|
|
||||||
let msk = high(t.data)
|
|
||||||
if i >= 0:
|
|
||||||
dec(t.counter)
|
|
||||||
block outer:
|
|
||||||
while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1
|
|
||||||
var j = i # The correctness of this depends on (h+1) in nextTry,
|
|
||||||
var r = j # though may be adaptable to other simple sequences.
|
|
||||||
t.data[i].hasValue = false # mark current EMPTY
|
|
||||||
t.data[i].key = ""
|
|
||||||
t.data[i].val = ""
|
|
||||||
while true:
|
|
||||||
i = (i + 1) and msk # increment mod table size
|
|
||||||
if not t.data[i].hasValue: # end of collision cluster; So all done
|
|
||||||
break outer
|
|
||||||
r = t.myhash(t.data[i].key) and msk # "home" location of key@i
|
|
||||||
if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
|
|
||||||
break
|
|
||||||
when defined(js):
|
|
||||||
t.data[j] = t.data[i]
|
|
||||||
else:
|
|
||||||
shallowCopy(t.data[j], t.data[i]) # data[j] will be marked EMPTY next loop
|
|
||||||
|
|
||||||
proc `$`*(t: StringTableRef): string {.rtlFunc, extern: "nstDollar".} =
|
|
||||||
## The `$` operator for string tables.
|
|
||||||
if t.len == 0:
|
|
||||||
result = "{:}"
|
|
||||||
else:
|
|
||||||
result = "{"
|
|
||||||
for key, val in pairs(t):
|
|
||||||
if result.len > 1: result.add(", ")
|
|
||||||
result.add(key)
|
|
||||||
result.add(": ")
|
|
||||||
result.add(val)
|
|
||||||
result.add("}")
|
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var x = {"k": "v", "11": "22", "565": "67"}.newStringTable
|
var x = {"k": "v", "11": "22", "565": "67"}.newStringTable
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue