# `Feel.Builtin.String`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/builtin/string.ex#L1)

The string functions of FEEL (DMN 1.6, section 10.3.4.3, Table 75).

## What is written (D-79)

All thirteen.

| Call | Result |
| ---- | ------ |
| `substring(string, start position, length)` | `length` characters from `start position` (1 is the first, -1 the last); all that is left without `length` |
| `string length(string)` | the number of code points |
| `upper case(string)`, `lower case(string)` | the full Unicode case mapping, with no locale |
| `substring before(string, match)`, `substring after(string, match)` | what is before or after the first occurrence of `match`; `""` if there is none |
| `contains(string, match)`, `starts with(string, match)`, `ends with(string, match)` | on the text; every string has the empty string in each of these places |
| `replace(input, pattern, replacement, flags)` | every match of the pattern replaced; `$1` is a group |
| `matches(input, pattern, flags)` | whether some part of the input matches the pattern |
| `split(string, delimiter)` | the parts between the matches of the delimiter, a pattern |
| `string join(list, delimiter)`, `string join(list)` | the strings of the list, `null`s left out, with the delimiter between them |

**Characters are code points**, as in the examples of the specification (`string length("\U01F40Eab")` is 3):
not UTF-16 units, and not the graphemes of `String.length/1`.

**`substring`** follows footnote 1 of the table: the start is a whole number (a fraction is cut off,
section 10.3.2.9.4) from `-L` to `L` and not 0, `L` being the length of the string, and the length from 1
to what is left. Anything else is `null` with an error, so `substring("foobar", 3, 10)` is `null` and so is
every call on `""` (notes, AE-21). A `null` length is no length.

**The patterns** of `replace`, `matches` and `split` are the regular expressions of XPath, read by
`Feel.Runtime.Regex` (D-80); one that is not in that dialect, a flag that is none of `smix`, a replacement
with a stray `$`, and (for `replace` and `split`) a pattern that matches the empty string, are `null` with
an error, which is what footnote 2 says to do with an error of XPath. A `null` flags is no flags. The
engine is stopped after `regex_match_limit` of `Feel.Limits`, and that ends the run.

**`match`** of `contains`, `substring before` and the like is a text and not a pattern.

**The size of the result** of `string join`, `replace` and `split` is worked out before the result is made, and a result
over `max_string_length` or `max_list_length` is refused with the error that the finished value would get
(`Feel.Builtin.Size`, `Feel.Runtime.Regex`). 5,000 strings of one byte joined by 100 KB are 500 MB, and the
replacement of every `a` of a megabyte of them by 100 KB is a hundred gigabytes: both are refused, and neither is made.

A `null` argument, and one of another kind, is out of the domain: the result is `null` with an
`:argument_out_of_domain` error. A list of one string is that string (section 10.3.2.9.4), and a string is
a list of one where `string join` wants a list.

# `functions`

```elixir
@spec functions() :: [Feel.Builtin.Function.t()]
```

The functions of this module that are written, as `Feel.Builtin.Function`s.

# `names`

```elixir
@spec names() :: [Feel.Name.t()]
```

Returns the canonical names of the functions of this module, sorted.

    iex> "string length" in Feel.Builtin.String.names()
    true

---

*Consult [api-reference.md](api-reference.md) for complete listing*
