regex
Regular expressions, of RE2’s (Go’s) syntax, matched as Go’s
regexp matches: leftmost first, each | and repetition preferring
what it is written to prefer, in time linear in the string (no
backtracking: the threads of an NFA run side by side, as RE2’s do).
What heff test --run selects tests by. A pattern is read once
(compile), and what it does not have is an error, not a literal, so
that a pattern means the same once more of the syntax is read.
- a rune stands for itself;
.for any rune but a line break; [abc],[a-z0-9]: one rune of the class;[^...]: one not of it (a]first in it is one of it, as a-first or last is);\d,\w,\s: a digit, a word’s rune ([0-9A-Za-z_]), a space ([\t\n\f\r ]), in a class too;\D,\W,\S: what they are not;\n,\t,\r,\f,\v: those runes;\before a rune that is not a letter or a digit: that rune (\.,\[,\\);^,\A: the start of the string;$,\z: its end;\b: where a word’s rune is on one side only;\B: where it is not;(...): a group, its match kept (numbered by its(, from 1);(?:...): a group that is not;- after an atom,
*,+,?,{n},{n,},{n,m}(n and m at most 1000): any number of it, one or more, one or none, n, n or more, n to m — as many as can be; each followed by?, as few; a|b: either, the first preferred.
Flags and named groups, (?i), (?P<name>...), and classes by
name, [[:alpha:]], \pL, are not read. Positions are of bytes, as
a string’s are; a rune of a string that is not UTF-8 is U+FFFD, a
byte wide, as runeAt reads it.
Regex
data RegexA pattern compiled: its program, and how many groups it numbers.
compile
compile : String -> Either String RegexThe pattern read, or what is wrong with it.
>>> List.map (\p -> either (\why -> why) (\r -> "read") (compile p)) ["a.c", "[a-", "x(y)", "*", "a**", "a(", "a)", "(?i)a", "a{2,1}", "\\q", "[\\D]"]["read", "a `[` with no `]`", "read", "a `*` with nothing before it to repeat", "a `*` after a repetition: repeat a group of it, `(?:...)`", "a `(` with no `)`", "a `)` with no `(`", "flags and named groups, `(?...)`, are not read", "a count backwards, `{n,m}` with m below n", "`\\q` is not read: escape only what is not a letter or a digit", "`\\D` in a class is not read"]compileLevels
compileLevels : String -> Either String (List Regex)The pattern read in levels, cut at each / that is not escaped, nor
in a class, nor in a group (Go’s -run A/B, a level a pattern):
each level read, or what is wrong with the first that is not.
>>> either (\why -> 0 - 1) (\rs -> List.length rs) (compileLevels "a/(b/c)/[/]")3matches
matches : Regex -> String -> BoolWhether the pattern matches somewhere in the string.
>>> List.map (\(p : Pair String String) -> either (\why -> False) (\r -> matches r (snd p)) (compile (fst p))) [MkPair "^lsp" "lsp hover", MkPair "^lsp" "the lsp", MkPair "hover$" "lsp hover", MkPair "a.c" "xabcx", MkPair "^a[0-9]+b$" "a12b", MkPair "^a[0-9]+b$" "ab", MkPair "x|^y" "y", MkPair "" "anything", MkPair "[^a-z]" "abc", MkPair "a\\.b" "axb"][True, False, True, True, True, False, True, True, False, False]>>> List.map (\(p : Pair String String) -> either (\why -> False) (\r -> matches r (snd p)) (compile (fst p))) [MkPair "^(ab)+$" "ababab", MkPair "^(ab)+$" "aba", MkPair "^a{2,3}$" "aaaa", MkPair "^\u{e9}.$" "\u{e9}\u{4e2d}", MkPair "\\bis\\b" "this is", MkPair "\\bis\\b" "this", MkPair "^\\d+\\s\\w+$" "42 heff_1", MkPair "^(a*)*$" "aaa", MkPair "^[]a]+$" "]a]"][True, False, False, True, True, False, True, True, True]findIndex
findIndex : Regex -> String -> Maybe (Int, Int)Where the leftmost match is, from its first byte up to its end, if
there is one (Go’s FindStringIndex).
>>> either (\why -> Nothing) (\r -> findIndex r "xx aab") (compile "a+b")Just (MkPair 3 6)find
find : Regex -> String -> Maybe StringThe leftmost match, if there is one: as many of each repetition as
can be, or as few, as it says, each | its first that matches.
>>> List.map (\p -> either (\why -> Nothing) (\r -> find r "<a><b>") (compile p)) ["<.*>", "<.*?>", "x"][Just "<a><b>", Just "<a>", Nothing]submatches
submatches : Regex -> String -> Maybe (List (Maybe String))The leftmost match and what each group matched in it, by its number
(the match the first, the group 0); a group that matched nothing,
not even an empty string, Nothing (Go’s FindStringSubmatch).
>>> either (\why -> Nothing) (\r -> submatches r "key = value") (compile "(\\w+) = (\\w+)|(none)")Just [Just "key = value", Just "key", Just "value", Nothing]>>> either (\why -> Nothing) (\r -> submatches r "abab") (compile "(a|b)+")Just [Just "abab", Just "b"]findAllIndex
findAllIndex : Regex -> String -> List (Int, Int)Where each match is, from the left, none overlapping (an empty one
right after another left out), as Go’s FindAllStringIndex finds
them.
>>> either (\why -> Nil) (\r -> findAllIndex r "baaab") (compile "a*")[MkPair 0 0, MkPair 1 4, MkPair 5 5]findAll
findAll : Regex -> String -> List StringEach match, as findAllIndex finds them.
>>> either (\why -> Nil) (\r -> findAll r "a1 b22 c333") (compile "[0-9]+")["1", "22", "333"]replaceAll
replaceAll : Regex -> String -> String -> StringEach match, as findAll finds them, the template, its $n and
${n} what the group n matched (none, or no such group, nothing),
its $$ a $ (Go’s ReplaceAllString; $1x is the group 1x,
which no group is, so write ${1}x).
>>> either (\why -> "") (\r -> replaceAll r "${2}=$1 $$" "a=1, b=2") (compile "(\\w+)=(\\w+)")"1=a $, 2=b $">>> either (\why -> "") (\r -> replaceAll r "-" "abc") (compile "")"-a-b-c-"replaceAllWith
replaceAllWith : Regex -> (String -> String) -> String -> StringEach match f of what it is (Go’s ReplaceAllStringFunc).
>>> either (\why -> "") (\r -> replaceAllWith r (\w -> showInt (strLength w)) "a bb ccc") (compile "[a-z]+")"1 2 3"split
split : Regex -> String -> List StringThe pieces between the matches, as Go’s Split with no limit cuts
them: an empty match at the start cuts no empty piece off, and an
empty match is cut at the end of none.
>>> List.map (\p -> either (\why -> Nil) (\r -> split r "a1b22c") (compile p)) ["[0-9]+", "", "x"][["a", "b", "c"], ["a", "1", "b", "2", "2", "c"], ["a1b22c"]]