Skip to content
heff

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 Regex

A pattern compiled: its program, and how many groups it numbers.

compile

compile : String -> Either String Regex

The 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)/[/]")
3

matches

matches : Regex -> String -> Bool

Whether 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 String

The 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 String

Each 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 -> String

Each 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 -> String

Each 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 String

The 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"]]