diff --git a/docs/functions.md b/docs/functions.md
index 6fc1433b..540da1f2 100644
--- a/docs/functions.md
+++ b/docs/functions.md
@@ -1,26 +1,34 @@
-## BoolExtension
-### fn **`bool_and`**(_bool_ **`a`**, _bool_ **`b`**) → _bool_
+## Stdlib\TypeExtensions\BoolTypeExtension
+### fn **`and`**(_bool_ **`a`**, _bool_ **`b`**) → _bool_
Returns result of logical `AND` between two boolean values.
```js
true.and(false) == false
true.and(true) == true
-bool_and(true, false) == false
+bool.and(true, false) == false
```
---
-### fn **`bool_not`**(_bool_ **`value`**) → _bool_
+### fn **`execute`**()
+
+ array Dict array containing Primi
+function/method object that represent type/class methods.
+
+---
+### fn **`not`**(_bool_ **`value`**) → _bool_
Returns a negation (logical `NOT`) of a single boolean value.
```js
false.not() == true
-bool_not(true) == false
+true.not() == false
+bool.not(false) == true
+bool.not(true) == false
```
---
-### fn **`bool_or`**(_bool_ **`a`**, _bool_ **`b`**) → _bool_
+### fn **`or`**(_bool_ **`a`**, _bool_ **`b`**) → _bool_
Returns an `OR` of two boolean values.
@@ -30,14 +38,14 @@ true.or(false) == true
false.or(true) == true
false.or(false) == false
-bool_or(true, true) == true
-bool_or(true, false) == true
-bool_or(false, true) == true
-bool_or(false, false) == false
+bool.or(true, true) == true
+bool.or(true, false) == true
+bool.or(false, true) == true
+bool.or(false, false) == false
```
---
-### fn **`bool_xor`**(_bool_ **`a`**, _bool_ **`b`**) → _bool_
+### fn **`xor`**(_bool_ **`a`**, _bool_ **`b`**) → _bool_
Returns an exclusive `OR` (`XOR`) of two boolean values.
@@ -47,167 +55,15 @@ true.xor(false) == true
false.xor(true) == true
false.xor(false) == false
-bool_xor(true, true) == false
-bool_xor(true, false) == true
-bool_xor(false, true) == true
-bool_xor(false, false) == false
-```
-
----
-## CastingExtension
-### fn **`to_bool`**(_any_ **`value`**) → _bool_
-
-Returns a new `bool` value based on argument's truthness.
-
-```js
-to_bool(true) == true
-to_bool(false) == false
-to_bool(-1) == true
-to_bool(0) == false
-to_bool([]) == false
-to_bool(['']) == true
-to_bool('') == false
-to_bool(' ') == true
-to_bool('0') == true
-to_bool('1') == true
-```
-
----
-### fn **`to_dict`**(_any_ **`value`**) → _dict_
-
-Returns a new dict containing items of some iterable value.
-
-```js
-a_list = 'máj'.to_dict()
-a_list == {0: 'm', 1: 'á', 2: 'j'}
-
-b_list = {'a': 1, 'b': 2, 'c': []}.to_list()
-b_list = [1, 2, []]
-
-c_list = {'a': 1, 'b': 2, 'c': []}.keys().to_list()
-c_list = ['a', 'b', 'c']
-```
-
----
-### fn **`to_list`**(_any_ **`value`**) → _list_
-
-Returns a new `list` containing items of some iterable value.
-
-```js
-a_list = 'první máj'.to_list()
-a_list == ["p", "r", "v", "n", "í", " ", "m", "á", "j"]
-
-b_list = {'a': 1, 'b': 2, 'c': []}.to_list()
-b_list = [1, 2, []]
-
-c_list = {'a': 1, 'b': 2, 'c': []}.keys().to_list()
-c_list = ['a', 'b', 'c']
-```
-
----
-### fn **`to_number`**(_any_ **`value`**) → _number_
-
-Cast a `number|string|bool` value to `number`.
-
-```js
-to_number(1) == 1
-to_number('123') == 123
-to_number('+123') == 123
-to_number('-123') == -123
-to_number(' +123.001 ') == 123.001
-to_number(' -123.00 ') == -123.0
-
-to_number(true) == 1
-to_number(false) == 0
-to_number(fal) == 0
-```
-
----
-### fn **`to_regex`**(_any_ **`value`**, *[_bool_ **`escape`**]*) → _regex_
-
-Convert `string` to a `regex` value. If the optional `escape` argument
-is `true`, the any characters with special regex meaning will be
-escaped so that they are meant literally.
-
-```js
-"hello".to_regex() == rx"hello"
-to_regex("Why so serious...?", true) == rx"Why so serious\.\.\.\?"
-```
-
----
-### fn **`to_string`**(_any_ **`value`**) → _string_
-
-Return a `string` representation of value.
-
-```js
-to_string(true) == 'true'
-to_string([]) == '[]'
-to_string(3.14) == '3.14'
-{'a': 1, 'b': 'c'}.to_string() == '{"a": 1, "b": "c"}'
-"hello there!".to_string() == "hello there!"
-to_string(to_string) == ""
+bool.xor(true, true) == false
+bool.xor(true, false) == true
+bool.xor(false, true) == true
+bool.xor(false, false) == false
```
---
-## CliExtension
-### fn **`debugger`**() → _any_
-
-_**Only in [CLI](https://w.wiki/QPE)**_.
-
-Injects a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)
-session for debugging at the specified line.
-
-
-
-
----
-### fn **`get_traceback`**() → _list_
-
-_**Only in [CLI](https://w.wiki/QPE)**_.
-
-Return traceback as a list.
-
-
-
-
----
-### fn **`memory_get_peak_usage`**() → _number_
-
-_**Only in [CLI](https://w.wiki/QPE)**_.
-
-Returns memory peak usage used by Primi _(engine behind the scenes)_ in
-bytes.
-
----
-### fn **`memory_get_usage`**() → _number_
-
-_**Only in [CLI](https://w.wiki/QPE)**_.
-
-Returns current memory usage used by Primi _(engine behind the scenes)_
-in bytes.
-
----
-### fn **`print`**(*[_any_ **`value`**]*, *[_bool_ **`nl`**]*) → _null_
-
-_**Only in [CLI](https://w.wiki/QPE)**_.
-
-Prints value to standard output.
-
----
-## DatetimeExtension
-### fn **`time_monotonic`**() → _number_
-
-Returns high-resolution monotonic time. It is an arbitrary number that
-keeps increasing by 1 every second.
-
----
-### fn **`time_unix`**() → _number_
-
-Returns high-resolution UNIX time.
-
----
-## DictExtension
-### fn **`dict_copy`**(_dict_ **`dict`**) → _dict_
+## Stdlib\TypeExtensions\DictTypeExtension
+### fn **`copy`**(_dict_ **`dict`**) → _dict_
Returns a new shallow copy of this dict.
@@ -221,7 +77,13 @@ b_dict == {'a': 1, 100: 'nope'}
```
---
-### fn **`dict_get`**(_dict_ **`dict`**, _any_ **`key`**, *[_any_ **`default`**]*) → _any_
+### fn **`execute`**()
+
+ array Dict array containing Primi
+function/method object that represent type/class methods.
+
+---
+### fn **`get`**(_dict_ **`dict`**, ___undefined___ **`key`**, *[___undefined___ **`default`**]*) → ___undefined___
Returns value stored under `key`, if it in dict, otherwise returns the
value of the `default` argument, which is `null` by default, but can
@@ -236,7 +98,7 @@ d.get('100', ['one', 'hundred']) == ['one', 'hundred']
```
---
-### fn **`dict_has_key`**(_dict_ **`dict`**, _any_ **`key`**) → _bool_
+### fn **`has_key`**(_dict_ **`dict`**, ___undefined___ **`key`**) → _bool_
Returns `true` if the key exists in dict. Return `false` otherwise.
@@ -249,7 +111,7 @@ d.has_key('yes') == false
```
---
-### fn **`dict_has_value`**(_dict_ **`dict`**, _any_ **`needle`**) → _bool_
+### fn **`has_value`**(_dict_ **`dict`**, ___undefined___ **`needle`**) → _bool_
Returns `true` if the value exists in dict. Return `false` otherwise.
@@ -262,7 +124,17 @@ d.has_value(false) == false
```
---
-### fn **`dict_keys`**(_dict_ **`dict`**) → _list_
+### fn **`items`**(_dict_ **`dict`**) → _list_
+
+Returns a new `list` of `tuples` of **key and value pairs** from this
+`dict`.
+
+```js
+{'a': 1, 100: 'yes'}.items() == [('a', 1), (100: 'yes')]
+```
+
+---
+### fn **`keys`**(_dict_ **`dict`**) → _list_
Returns a new `list` containing **keys** from this `dict`.
@@ -271,7 +143,7 @@ Returns a new `list` containing **keys** from this `dict`.
```
---
-### fn **`dict_map`**(_dict_ **`dict`**, _function_ **`fn`**) → _dict_
+### fn **`map`**() → _dict_
Returns a new dict with same keys but values returned by a passed
function _(callback)_ applied to each item.
@@ -284,19 +156,8 @@ fn = (v, k) => { return k + "|" + v; }
a_dict.map(fn) == {"key_a": "key_a|val_a", "key_b": "key_b|val_b"}
```
-
-
----
-### fn **`dict_reverse`**(_dict_ **`dict`**) → _any_
-
-Returns a new `dict` with original `dict`'s items in reversed order.
-
-```js
-{'a': 1, 100: 'yes'}.reverse() == {100: 'yes', 'a': 1}
-```
-
---
-### fn **`dict_values`**(_dict_ **`dict`**) → _list_
+### fn **`values`**(_dict_ **`dict`**) → _list_
Returns a new `list` containing **values** from this `dict`.
@@ -305,31 +166,15 @@ Returns a new `list` containing **values** from this `dict`.
```
---
-## HashExtension
-### fn **`hash_md5`**(_string_ **`val`**) → _string_
-
-Return [`md5` hash](https://en.wikipedia.org/wiki/MD5) representation
-of a `string` value as `string`.
-
-```js
-hash_md5('hello') == '5d41402abc4b2a76b9719d911017c592'
-hash_md5('123') == '202cb962ac59075b964b07152d234b70'
-```
-
----
-### fn **`hash_sha256`**(_string_ **`val`**) → _string_
-
-Return [`sha256` hash](https://en.wikipedia.org/wiki/SHA-2) representation
-of a `string` value as `string`.
+## Stdlib\TypeExtensions\ForbiddenTypeExtension
+### fn **`execute`**()
-```js
-hash_sha256('hello') == '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
-hash_sha256('123') == 'a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3'
-```
+ array Dict array containing Primi
+function/method object that represent type/class methods.
---
-## ListExtension
-### fn **`list_contains`**(_list_ **`list`**, _any_ **`needle`**) → _bool_
+## Stdlib\TypeExtensions\ListTypeExtension
+### fn **`contains`**(_list_ **`list`**, ___undefined___ **`needle`**) → _bool_
Returns `true` if the `needle` is present in the `list` at least once.
@@ -346,12 +191,12 @@ Returns `true` if the `needle` is present in the `list` at least once.
```
---
-### fn **`list_copy`**(_list_ **`list`**) → _list_
+### fn **`copy`**(_list_ **`list`**) → _list_
Returns a new copy of the `list`.
---
-### fn **`list_count`**(_list_ **`list`**, _any_ **`needle`**) → _number_
+### fn **`count`**(_list_ **`list`**, ___undefined___ **`needle`**) → _number_
Returns number of occurrences of some value in the `list`.
@@ -368,7 +213,13 @@ Returns number of occurrences of some value in the `list`.
```
---
-### fn **`list_get`**(_list_ **`list`**, _number_ **`index`**, *[_any_ **`default`**]*) → _any_
+### fn **`execute`**()
+
+ array Dict array containing Primi
+function/method object that represent type/class methods.
+
+---
+### fn **`get`**(_list_ **`list`**, _number_ **`index`**, *[___undefined___ **`default`**]*) → ___undefined___
Returns an item from `list` by its index _(starting at 0)_. Negative
indexes can be used to get items from the end.
@@ -393,7 +244,7 @@ by default)_
```
---
-### fn **`list_map`**(_list_ **`list`**, _function_ **`fn`**) → _list_
+### fn **`map`**() → _list_
Returns a new `list` from results of a passed function _(callback)_
applied to each item.
@@ -404,10 +255,8 @@ Callback arguments: `callback(value)`.
[-1, 0, 2].map(to_bool) == [true, false, true]
```
-
-
---
-### fn **`list_pop`**(_list_ **`list`**, *[_number_ **`index`**]*) → _any_
+### fn **`pop`**(_list_ **`list`**, *[_number_ **`index`**]*) → ___undefined___
Remove (pop) item at specified `index` from the `list` and return it.
@@ -423,7 +272,7 @@ a_list.pop(-3) == 1 // a_list == [3, 4], 1 is returned
```
---
-### fn **`list_prepend`**(_list_ **`list`**, _any_ **`value`**) → _null_
+### fn **`prepend`**(_list_ **`list`**, ___undefined___ **`value`**) → _null_
Prepend an item to the beginning of the `list`.
@@ -434,7 +283,7 @@ a_list == [{'some_key': 'some_value'}, 'a', 'b', 'c']
```
---
-### fn **`list_push`**(_list_ **`list`**, _any_ **`value`**) → _null_
+### fn **`push`**(_list_ **`list`**, ___undefined___ **`value`**) → _null_
Add (push) an item to the end of the `list`.
@@ -445,7 +294,7 @@ a_list == ['a', 'b', 'c', {'some_key': 'some_value'}]
```
---
-### fn **`list_random`**(_list_ **`list`**) → _any_
+### fn **`random`**(_list_ **`list`**) → ___undefined___
Returns a random item from the `list`.
@@ -454,7 +303,7 @@ Returns a random item from the `list`.
```
---
-### fn **`list_reverse`**(_list_ **`list`**) → _list_
+### fn **`reverse`**(_list_ **`list`**) → _list_
Returns a new `list` with values of the original `list` reversed.
@@ -463,7 +312,7 @@ Returns a new `list` with values of the original `list` reversed.
```
---
-### fn **`list_shuffle`**(_list_ **`list`**) → _list_
+### fn **`shuffle`**(_list_ **`list`**) → _list_
Returns a new `list` with shuffled items.
@@ -472,141 +321,79 @@ Returns a new `list` with shuffled items.
```
---
-## NumberExtension
-### fn **`number_abs`**(_number_ **`n`**) → _number_
-
-Returns the absolute value of number `n`.
-
----
-### fn **`number_atan`**(_number_ **`n`**) → _number_
-
-Returns the arc tangent of number `n` specified in radians.
-
----
-### fn **`number_ceil`**(_number_ **`n`**) → _number_
+## Stdlib\TypeExtensions\NullTypeExtension
+### fn **`execute`**()
-Returns number `n` rounded up.
+ array Dict array containing Primi
+function/method object that represent type/class methods.
---
-### fn **`number_cos`**(_number_ **`n`**) → _number_
+## Stdlib\TypeExtensions\NumberTypeExtension
+### fn **`execute`**()
-Returns the cosine of number `n` specified in radians.
+ array Dict array containing Primi
+function/method object that represent type/class methods.
---
-### fn **`number_divisible_by`**(_number_ **`a`**, _number_ **`b`**) → _bool_
+### fn **`is_divisible_by`**(_number_ **`a`**, _number_ **`b`**) → _bool_
Return `true` if first argument is divisible by the second argument.
---
-### fn **`number_floor`**(_number_ **`n`**) → _number_
-
-Returns number `n` rounded down.
-
----
-### fn **`number_mod`**(_number_ **`a`**, _number_ **`b`**) → _number_
-
-Returns the remainder (modulo) of the division of the arguments.
-
----
-### fn **`number_pow`**(_number_ **`n`**, *[_number_ **`power`**]*) → _number_
-
-Returns number `n` squared to the power of `power`.
-
----
-### fn **`number_round`**(_number_ **`n`**, *[_number_ **`precision`**]*) → _number_
-
-Returns number `n` rounded to specified `precision`. If the
-precision is not specified, a default `precision` of zero is used.
-
----
-### fn **`number_sin`**(_number_ **`n`**) → _number_
+## Stdlib\TypeExtensions\ObjectTypeExtension
+### fn **`execute`**()
-Returns the sine of number `n` specified in radians.
+ array Dict array containing Primi
+function/method object that represent type/class methods.
---
-### fn **`number_sqrt`**(_number_ **`n`**) → _number_
+## Stdlib\TypeExtensions\RegexTypeExtension
+### fn **`execute`**()
-Returns the square root of a number `n`.
+ array Dict array containing Primi
+function/method object that represent type/class methods.
---
-### fn **`number_tan`**(_number_ **`n`**) → _number_
+### fn **`find`**(_regex_ **`regex`**, _string_ **`haystack`**) → _string|bool_
-Returns the tangent of number `n` specified in radians.
-
----
-## RegexExtension
-### fn **`regex_match`**(_regex_ **`regex`**, _string_ **`haystack`**) → _any_
-
-Regular expression match. Returns the first matching string. Otherwise
-returns `false`.
+Regular expression find. Returns the first occurence of matching string.
+Otherwise returns `false`.
```js
-rx"[xyz]+".match("abbcxxyzzdeef") == "xxyzz"
+rx"[xyz]+".find("abbcxxyzzdeef") == "xxyzz"
```
---
-## StandardExtension
-### fn **`assert`**(_bool_ **`assumption`**, *[_string_ **`description`**]*) → _bool_
-
-This function returns `true` if a `bool` value passed into it is `true`
-and throws error if it's `false`. Optional `string` description can be
-provided, which will be visible in the eventual error message.
-
----
-### fn **`len`**(_any_ **`value`**) → _number_
+## Stdlib\TypeExtensions\StringTypeExtension
+### fn **`contains`**(_string_ **`haystack`**, ___undefined___ **`needle`**) → _bool_
-Returns length of a value.
-
-```js
-"hello, Česká Třebová".len() == 20
-len(123456) == 6
-[1, 2, 3].len() == 3
-len({'a': 1, 'b': 'c'}) == 2
-```
-
----
-### fn **`range`**(_number_ **`start`**, *[_number_ **`end`**]*, *[_number_ **`step`**]*) → _list_
-
-Return a list containing numbers from `start` to `end` _(including)_.
-If the `end` is not specified, numbers from `0` to the value of the
-first argument `start` is returned. Optional third argument `step` can
-be specified for steps other than `1`.
+Returns `true` if the `string` contains `needle`. Returns `false`
+otherwise.
```js
-range(0) == [0]
-range(1) == [0, 1]
-range(5) == [0, 1, 2, 3, 4, 5]
-range(2, 7) == [2, 3, 4, 5, 6, 7]
-range(2, -7) == [2, 1, 0, -1, -2, -3, -4, -5, -6, -7]
-range(2, -7, 3) == [2, -1, -4, -7]
-range(5, 10, 3) == [5, 8] // The last number that could be obtained by incrementing 3 is 8.
+"this is a sentence".contains("sen") == true
+"this is a sentence".contains("yay") == false
```
---
-### fn **`type`**(_any_ **`value`**) → _string_
+### fn **`ends_with`**(_string_ **`haystack`**, _string_ **`needle`**) → _bool_
-Return type of value as string.
+Returns `true` if the string ends with specified string suffix.
+Returns `false` otherwise.
```js
-type(true) == 'bool'
-type("hello") == 'string'
-type(type) == 'function'
+"this is a sentence".ends_with("tence") == true
+"this is a sentence".ends_with("e") == true
+"this is a sentence".ends_with("x") == false
```
---
-## StringExtension
-### fn **`string_contains`**(_string_ **`haystack`**, _any_ **`needle`**) → _bool_
-
-Returns `true` if the `string` contains `needle`. Returns `false`
-otherwise.
+### fn **`execute`**()
-```js
-"this is a sentence".contains("sen") == true
-"this is a sentence".contains("yay") == false
-```
+_Missing description._
---
-### fn **`string_find_first`**(_string_ **`haystack`**, _any_ **`needle`**) → _any_
+### fn **`find_first`**(_string_ **`haystack`**, ___undefined___ **`needle`**) → ___undefined___
Returns the position _(index)_ of **first** occurrence of `needle` in
the `string`. If the `needle` was not found, `null` is returned.
@@ -618,7 +405,7 @@ the `string`. If the `needle` was not found, `null` is returned.
```
---
-### fn **`string_find_last`**(_string_ **`haystack`**, _any_ **`needle`**) → _any_
+### fn **`find_last`**(_string_ **`haystack`**, ___undefined___ **`needle`**) → ___undefined___
Returns the position _(index)_ of **last** occurrence of `needle` in
the `string`. If the `needle` was not found, `null` is returned.
@@ -630,7 +417,7 @@ the `string`. If the `needle` was not found, `null` is returned.
```
---
-### fn **`string_format`**(_string_ **`str`**, *[_any_ **`items`**]*) → _string_
+### fn **`format`**(_string_ **`str`**, *[___undefined___ **`items`**]*) → _string_
Returns a new `string` with placeholders from the original `string`
replaced by additional arguments.
@@ -645,7 +432,7 @@ Placeholders can be either _(but these can't be combined)_:
```
---
-### fn **`string_join`**(_string_ **`string`**, _any_ **`iterable`**) → _string_
+### fn **`join`**(_string_ **`string`**, ___undefined___ **`iterable`**) → _string_
Join items from `iterable` with this `string` and return the result as
a new string.
@@ -657,7 +444,7 @@ a new string.
```
---
-### fn **`string_number_of`**(_string_ **`haystack`**, _any_ **`needle`**) → _number_
+### fn **`number_of`**(_string_ **`haystack`**, ___undefined___ **`needle`**) → _number_
Returns `number` of occurrences of `needle` in a string.
@@ -667,22 +454,18 @@ Returns `number` of occurrences of `needle` in a string.
```
---
-### fn **`string_replace`**(_string_ **`string`**, _any_ **`search`**, *[_string_ **`replace`**]*) → _string_
+### fn **`replace`**(_string_ **`string`**, ___undefined___ **`search`**, _string_ **`replace`**) → _string_
Perform search and replace and return the results as new `string`.
-Two separate modes of operation:
-1. The needle `search` is a `string` and haystack `replace` is a string.
-2. The needle `search` is a `dict` defining search-and-replace pairs
-_(and `replace` argument is omitted)_.
-
```js
"abcdef".replace("c", "X") == "abXdef"
-"abcdef".replace({"c": "X", "e": "Y"}) == "abXdYf"
+"přítmí ve městě za dvě stě".replace("stě", "šci") == "přítmí ve měšci za dvě šci"
+"přítmí ve městě za dvě stě".replace(rx"\wt\w", "lol") == "přlolí ve mělol za dvě lol"
```
---
-### fn **`string_reverse`**(_string_ **`string`**) → _string_
+### fn **`reverse`**(_string_ **`string`**) → _string_
Return reversed string.
@@ -691,7 +474,7 @@ Return reversed string.
```
---
-### fn **`string_shuffle`**(_string_ **`str`**) → _string_
+### fn **`shuffle`**(_string_ **`str`**) → _string_
Returns a new `string` from shuffled characters of the original `string`.
@@ -700,7 +483,7 @@ Returns a new `string` from shuffled characters of the original `string`.
```
---
-### fn **`string_split`**(_string_ **`string`**, *[_any_ **`delimiter`**]*) → _list_
+### fn **`split`**() → _list_
Split original `string` by some `delimiter` and return result the as a
`list`. If the `delimiter` is not specified, the `string` is splat by
@@ -712,3 +495,60 @@ whitespace characters.
```
---
+### fn **`starts_with`**(_string_ **`haystack`**, _string_ **`needle`**) → _bool_
+
+Returns `true` if the string starts with specified string.
+Returns `false` otherwise.
+
+```js
+"this is a sentence".starts_with("this") == true
+"this is a sentence".starts_with("t") == true
+"this is a sentence".starts_with("x") == false
+```
+
+---
+### fn **`substring`**(_string_ **`string`**, _number_ **`offset`**, *[_number_ **`length`**]*) → _string_
+
+Returns a substring taken from `string` starting at `offset` with length `length`.
+Access outside of bounds will not result in errors.
+Length can be omitted to reach up to the `string` end.
+Negative offset can be used.
+
+```js
+"this is a sentence".substring(5, 4) == "is a"
+"this is a sentence".substring(5) == "is a sentence"
+"this is a sentence".substring(10, 100) == "sentence"
+"this is a sentence".substring(-8) == "sentence"
+"this is a sentence".substring(20, 15) == ""
+```
+
+---
+### fn **`translate`**(_string_ **`string`**, _dict_ **`pairs`**) → _string_
+
+Search and replace strings within a string and return the new resulting
+string. The from-to pairs are to be provided as a `dict`.
+
+```js
+"abcdef".replace({'c': 'X', 'e': 'Y'}) == "abXdYf"
+"abcdef".replace({'b': 'X', 'ab': 'Y'}) == "Ycdef"
+```
+
+The longest keys will be tried first. Once a substring has been replaced,
+its new value will not be searched again. This behavior is identical
+to PHP function [`strtr()`](https://www.php.net/manual/en/function.strtr.php).
+
+---
+## Stdlib\TypeExtensions\TupleTypeExtension
+### fn **`execute`**()
+
+ array Dict array containing Primi
+function/method object that represent type/class methods.
+
+---
+## Stdlib\TypeExtensions\TypeTypeExtension
+### fn **`execute`**()
+
+ array Dict array containing Primi
+function/method object that represent type/class methods.
+
+---
diff --git a/src/Helpers/StringEscaping.php b/src/Helpers/StringEscaping.php
index 27f7fb80..0fbbd33a 100644
--- a/src/Helpers/StringEscaping.php
+++ b/src/Helpers/StringEscaping.php
@@ -79,7 +79,7 @@ public static function unescapeString(string $str): string {
*/
public static function escapeString(
string $str,
- string $quoteChar = \null
+ ?string $quoteChar = \null
): string {
foreach (self::ESCAPE_PAIRS as $out => $in) {
diff --git a/src/Repl.php b/src/Repl.php
index 679e1422..997cb1e4 100644
--- a/src/Repl.php
+++ b/src/Repl.php
@@ -63,7 +63,7 @@ class Repl {
public function __construct(
?string $replName = null,
- ReplIoDriverInterface $driver = null
+ ?ReplIoDriverInterface $driver = null
) {
self::$historyFilePath = getenv("HOME") . '/' . self::HISTORY_FILE;
diff --git a/src/Stdlib/Modules/std/math.primi.php b/src/Stdlib/Modules/std/math.primi.php
index 05a5a5ee..2589a062 100644
--- a/src/Stdlib/Modules/std/math.primi.php
+++ b/src/Stdlib/Modules/std/math.primi.php
@@ -122,7 +122,7 @@ public static function atan(NumberValue $n): NumberValue {
#[PrimiFunc]
public static function round(
NumberValue $n,
- NumberValue $precision = \null
+ ?NumberValue $precision = \null
): NumberValue {
return Interned::number((string) \round(
(float) $n->value,
diff --git a/src/Stdlib/TypeExtensions/BoolTypeExtension.php b/src/Stdlib/TypeExtensions/BoolTypeExtension.php
index ca94b649..98317439 100644
--- a/src/Stdlib/TypeExtensions/BoolTypeExtension.php
+++ b/src/Stdlib/TypeExtensions/BoolTypeExtension.php
@@ -52,7 +52,7 @@ public static function not(BoolValue $value): BoolValue {
* ```js
* true.and(false) == false
* true.and(true) == true
- * bool_and(true, false) == false
+ * bool.and(true, false) == false
* ```
*/
#[PrimiFunc]
@@ -69,10 +69,10 @@ public static function and(BoolValue $a, BoolValue $b): BoolValue {
* false.or(true) == true
* false.or(false) == false
*
- * bool_or(true, true) == true
- * bool_or(true, false) == true
- * bool_or(false, true) == true
- * bool_or(false, false) == false
+ * bool.or(true, true) == true
+ * bool.or(true, false) == true
+ * bool.or(false, true) == true
+ * bool.or(false, false) == false
* ```
*/
#[PrimiFunc]
@@ -89,10 +89,10 @@ public static function or(BoolValue $a, BoolValue $b): BoolValue {
* false.xor(true) == true
* false.xor(false) == false
*
- * bool_xor(true, true) == false
- * bool_xor(true, false) == true
- * bool_xor(false, true) == true
- * bool_xor(false, false) == false
+ * bool.xor(true, true) == false
+ * bool.xor(true, false) == true
+ * bool.xor(false, true) == true
+ * bool.xor(false, false) == false
* ```
*/
#[PrimiFunc]
diff --git a/src/Stdlib/TypeExtensions/ListTypeExtension.php b/src/Stdlib/TypeExtensions/ListTypeExtension.php
index 83e5a671..32218aa8 100644
--- a/src/Stdlib/TypeExtensions/ListTypeExtension.php
+++ b/src/Stdlib/TypeExtensions/ListTypeExtension.php
@@ -206,7 +206,7 @@ public static function contains(
public static function get(
ListValue $list,
NumberValue $index,
- AbstractValue $default = \null
+ ?AbstractValue $default = \null
): AbstractValue {
// If the index is not found, this will return null.
diff --git a/src/Stdlib/TypeExtensions/StringTypeExtension.php b/src/Stdlib/TypeExtensions/StringTypeExtension.php
index a2dffc8b..c0d5fe80 100644
--- a/src/Stdlib/TypeExtensions/StringTypeExtension.php
+++ b/src/Stdlib/TypeExtensions/StringTypeExtension.php
@@ -482,10 +482,11 @@ public static function join(
/**
* Returns `true` if the string starts with specified string.
- *
+ * Returns `false` otherwise.
+ *
* ```js
- * "this is a sentence".starts_with("tence") == true
- * "this is a sentence".starts_with("e") == true
+ * "this is a sentence".starts_with("this") == true
+ * "this is a sentence".starts_with("t") == true
* "this is a sentence".starts_with("x") == false
* ```
*/
@@ -499,7 +500,8 @@ public static function starts_with(
/**
* Returns `true` if the string ends with specified string suffix.
- *
+ * Returns `false` otherwise.
+ *
* ```js
* "this is a sentence".ends_with("tence") == true
* "this is a sentence".ends_with("e") == true
@@ -514,4 +516,35 @@ public static function ends_with(
return Interned::bool(\str_ends_with($haystack->value, $needle->value));
}
+ /**
+ * Returns a substring taken from `string` starting at `offset` with length `length`.
+ * Access outside of bounds will be silently tolerated.
+ * Length can be omitted to reach up to the `string` end.
+ * Negative offset can be used.
+ *
+ * ```js
+ * "this is a sentence".substring(5, 4) == "is a"
+ * "this is a sentence".substring(5) == "is a sentence"
+ * "this is a sentence".substring(10, 100) == "sentence"
+ * "this is a sentence".substring(-8) == "sentence"
+ * "this is a sentence".substring(20, 15) == ""
+ * ```
+ */
+ #[PrimiFunc]
+ public static function substring(
+ StringValue $string,
+ NumberValue $offset,
+ ?NumberValue $length = \null,
+ ): StringValue {
+ $input = $string->value;
+ $start = $offset->value ?? 0;
+ $len = $length?->value ?? \mb_strlen($input);
+
+ if ($start < 0) {
+ $start = \mb_strlen($input) + $start;
+ }
+
+ return new StringValue(\mb_substr($input, (int)$start, (int)$len));
+ }
+
}
diff --git a/tools/docgen/docgen.php b/tools/docgen/docgen.php
index 40976a30..d56ed177 100644
--- a/tools/docgen/docgen.php
+++ b/tools/docgen/docgen.php
@@ -64,7 +64,7 @@ function err($text): void {
function get_relevant_methods(string $className): array {
- $classRef = new \ReflectionClass("\Smuuf\Primi\Stdlib\\Extensions\\{$className}");
+ $classRef = new \ReflectionClass("\Smuuf\Primi\\{$className}");
// We want methods that are both public AND static AND non-PHP-magic.
return array_filter(
@@ -146,7 +146,7 @@ function extract_params(\ReflectionMethod $methodRef): array {
line(Colors::get("- File {cyan}$filepath{_}"));
$filename = basename($filepath);
- $className = substr($filename, 0, strrpos($filename, '.'));
+ $className = str_replace("/", "\\", substr($filepath, 6, strrpos($filepath, '.') - 6));
$methods = get_relevant_methods($className);
$data[$className] = [];
@@ -168,7 +168,16 @@ function extract_params(\ReflectionMethod $methodRef): array {
$returnType = false;
if ($returnTypeRef = $methodRef->getReturnType()) {
try {
- $returnType = ($returnTypeRef->getName())::TYPE;
+ $returnType = match (true) {
+ $returnTypeRef instanceof ReflectionUnionType => implode(
+ "|",
+ array_map(
+ fn(ReflectionNamedType $type) => $type->getName()::TYPE,
+ $returnTypeRef->getTypes(),
+ ),
+ ),
+ default => ($returnTypeRef->getName())::TYPE,
+ };
} catch (\Throwable $e) {
warn("Class '$className, method '$methodName', referencing non-existent Primi type having class " . $returnTypeRef->getName());
}