(std rx)

Reference

Overview

Regular expressions written as s-expressions, checked and compiled at compile time.

(import (std rx))

(def phone #rx(seq :bos
                   (=> :area (= 3 :ascii-digit))   ; three digits, named :area
                   (lit "-")
                   (=> :line (= 4 :ascii-digit))   ; four digits, named :line
                   :eos))

(rx-match? phone "555-1234")          ; #t
(rx-search #rx((+ :digit)) "a42b")    ; (Some <match on "42">)

A pattern is written #rx(...) in a notation like Scheme’s SRE, not as a string such as "[a-z]+", so nothing is ever escaped twice. It is read, checked and compiled to a pattern at compile time, and a bad pattern is a compile error pointing at the sub-form that is wrong. The pattern language is described with #rx.

Using a pattern. A #rx(...) is an Rx, a compiled regular expression. Compiling is cached by pattern, so evaluating the same #rx(...) again does not build a second regex. rx-match? and rx-match match the whole string; rx-search? and rx-search find the leftmost match anywhere in it; rx-matches finds them all. A match is an RxMatch, read with rx-text, rx-start, rx-end and the rx-group family. rx-replace, rx-replace-with and rx-split rewrite a string. In a match, the patterns Rx and RxIn do the same as rx-match and rx-search.

The engine. The matching is BjoRx, a translation of Rust’s regex crate: it matches in time linear in the length of the string, over the string’s UTF-8 as it is, and a match is the leftmost one, with alternatives tried in the order written. Only ($ ...) and (=> :name ...) capture. It has no backreferences, lookarounds, atomic groups or conditionals, the constructs that would tie Bjolang to a backtracking engine for good. So these are not supported, on purpose:

Unicode. The alphabet is the Unicode scalar, as it is for char and the string cursors: :any is one character, a match never begins or ends inside one, and a character set may hold any scalar, (in "😀") included.

The named sets are written out as explicit ranges from tables in rx.bjo, generated from .NET’s CharUnicodeInfo so that :alpha agrees with char-alphabetic? below U+10000. A compiled program means the same thing on every runtime.

See also: rx-match?, rx-search, rx-compile, RxMatch

Reference

Types

Functions

Macros

Types

RxMatch

type alias ↑ Types

One match: the string it was found in, where, and its groups.

See also: rx-text, rx-start, rx-group, rx-group/n

Functions

rx-match

function ↑ Functions

(: rx-match (-> Rx string (Option RxMatch)))
(rx-match rx s)

Matches a pattern against the whole string.

rx
The pattern.
s
The string.
returns
The match, anchored at both ends, or None.
(match (rx-match phone "555-1234")
  ((Some m) (rx-group m :area))   ; (Some "555")
  (None None))

See also: rx-match?, rx-search, Rx

rx-match?

function ↑ Functions

(: rx-match? (-> Rx string bool))
(rx-match? rx s)

Whether a pattern matches the whole string.

rx
The pattern.
s
The string.
returns
#t if the whole of s matches.
(rx-match? #rx((+ :ascii-digit)) "123")    ; #t
(rx-match? #rx((+ :ascii-digit)) "123a")   ; #f

See also: rx-match, rx-search?

function ↑ Functions

(: rx-search (-> Rx string (Option RxMatch)))
(rx-search rx s)

The leftmost match anywhere in the string.

rx
The pattern.
s
The string.
returns
The leftmost match, or None.
(rx-search #rx((+ :digit)) "a42b")   ; (Some <match on "42">)

See also: rx-search?, rx-match, rx-matches, RxIn

rx-search?

function ↑ Functions

(: rx-search? (-> Rx string bool))
(rx-search? rx s)

Whether a pattern matches anywhere in the string.

rx
The pattern.
s
The string.
returns
#t if some part of s matches.

See also: rx-search, rx-match?

rx-matches

function ↑ Functions

(: rx-matches (-> Rx string (Vec RxMatch)))
(rx-matches rx s)

Every non-overlapping match, left to right.

rx
The pattern.
s
The string.
returns
The matches, in order; empty if there are none.
(vec-map rx-text (rx-matches #rx((+ :digit)) "a1b22c333"))   ; ["1" "22" "333"]

Each search starts where the last match ended. After an empty match it starts one character on, so it never starts inside one.

See also: rx-search, rx-split

rx-replace

function ↑ Functions

(: rx-replace (-> Rx string string string))
(rx-replace rx s replacement)

The string with every match replaced by a fixed text.

rx
The pattern.
s
The string.
replacement
The text to put in place of each match, taken literally.
returns
The new string.
(rx-replace #rx((+ :space)) "a  b   c" " ")   ; "a b c"

There is no $1 substitution syntax: that would be a second pattern language, arriving without being decided on. Use rx-replace-with to build the replacement from the match.

See also: rx-replace-with

rx-replace-with

function ↑ Functions

(: rx-replace-with (-> Rx string (-> RxMatch string) string))
(rx-replace-with rx s f)

The string with every match replaced by what a function makes of it.

rx
The pattern.
s
The string.
f
Function taking a match and returning its replacement string.
returns
The new string.
(rx-replace-with #rx(($ (+ :ascii-digit)))
                 "3 and 4"
                 (fun (m) (str "<" (rx-text m) ">")))   ; "<3> and <4>"

See also: rx-replace

rx-split

function ↑ Functions

(: rx-split (-> Rx string (Vec string)))
(rx-split rx s)

The pieces of the string between the matches.

rx
The separator pattern.
s
The string.
returns
The pieces, in order. The empty pieces before a leading and after a trailing separator are kept; with no match, the whole string.
(rx-split #rx(seq "," (* :space)) "a, b,c")   ; ["a" "b" "c"]

An empty match is not a separator, so a pattern that can match nothing does not split between every character.

See also: rx-matches

rx-text

function ↑ Functions

(: rx-text (-> RxMatch string))
(rx-text m)

The text a match matched.

m
The match.
returns
The matched part of the string.

See also: rx-start, rx-group

rx-input

function ↑ Functions

(: rx-input (-> RxMatch string))
(rx-input m)

The string a match was found in.

m
The match.
returns
The whole string that was searched, which its cursors index.

See also: rx-start, rx-end

rx-start

function ↑ Functions

(: rx-start (-> RxMatch StringCursor))
(rx-start m)

Where a match begins.

m
The match.
returns
A cursor into rx-input, on the match's first character.

See also: rx-end, rx-input

rx-end

function ↑ Functions

(: rx-end (-> RxMatch StringCursor))
(rx-end m)

Where a match ends.

m
The match.
returns
A cursor into rx-input, just after the match's last character.

See also: rx-start, rx-input

rx-group

function ↑ Functions

(: rx-group (-> RxMatch Keyword (Option string)))
(rx-group m name)

The text a named group captured.

m
The match.
name
The keyword the group was named with, in (=> :name ...).
returns
The captured text, or None if the group took no part in the match. An unknown name is None too.
(rx-group m :area)   ; (Some "555")

See also: rx-group/n, rx-group-start, rx-group-end

rx-group-start

function ↑ Functions

(: rx-group-start (-> RxMatch Keyword (Option StringCursor)))
(rx-group-start m name)

Where a named group’s capture begins.

m
The match.
name
The keyword the group was named with.
returns
A cursor into rx-input, or None if the group took no part in the match or there is no such group.

See also: rx-group, rx-group-end

rx-group-end

function ↑ Functions

(: rx-group-end (-> RxMatch Keyword (Option StringCursor)))
(rx-group-end m name)

Where a named group’s capture ends.

m
The match.
name
The keyword the group was named with.
returns
A cursor into rx-input just after the capture, or None if the group took no part in the match or there is no such group.

See also: rx-group, rx-group-start

rx-group/n

function ↑ Functions

(: rx-group/n (-> RxMatch int (Option string)))
(rx-group/n m n)

The text a group captured, by its number.

m
The match.
n
The group's number, counted from zero in the order the groups open. Named and unnamed groups are counted alike.
returns
The captured text, or None if the group took no part in the match or there is no such group.
(match (rx-search #rx(seq ($ (+ :ascii-alpha)) "=" ($ (+ :ascii-digit))) "x=42")
  ((Some m) (rx-group/n m 1))   ; (Some "42")
  (None None))

See also: rx-group, rx-group-count

rx-group-count

function ↑ Functions

(: rx-group-count (-> RxMatch int))
(rx-group-count m)

How many groups the pattern has.

m
The match.
returns
The number of groups, named and unnamed, whether or not they took part in this match.

See also: rx-group/n

rx-compile

function ↑ Functions

(: rx-compile (-> string string Rx))
(rx-compile pattern names)

Compiles a pattern into an Rx: what #rx(…) expands into.

pattern
The pattern, as #rx writes it: BjoRx's tree as text.
names
The Bjolang name of each group in order, separated by commas. An unnamed group has an empty name.
returns
The compiled regular expression.
raises System.ArgumentException
When pattern is not a pattern as #rx writes them.

An Rx is a compiled regular expression. Compiling is cached by the pattern and the names, so the same pattern compiled twice is one regex.

It is there for #rx, which checks what it emits. The text is one parenthesized form per node: (l 61 62) a literal, (c 30-39) a class of scalar ranges, (s ...) a sequence, (a ...) an alternation, (r least most g pattern) a repetition (- for no bound, l for lazy), (g n pattern) group n counting from 1, (A) and (z) the start and end, (e) nothing. Scalars are hexadecimal. Commas cannot occur in a keyword, so they cannot occur in a name.

See also: rx-pattern

rx-pattern

function ↑ Functions

(: rx-pattern (-> Rx string))
(rx-pattern rx)

The pattern an Rx was compiled from.

rx
The compiled regular expression.
returns
The pattern text, so that a test can check what was emitted and not only what it matches.

See also: rx-compile

Macros

#rx

reader extension ↑ Macros

#rx(pattern)

A regular expression, checked and compiled at compile time.

pattern
The pattern, in the notation below.
#rx(seq :bos (+ :digit) (lit "-") ($ (** 4 4 :digit)) :eos)
#rx(:digit)              ; one argument is the pattern itself
#rx((+ :digit))
#rx(+ :digit)            ; and the same as #rx((+ :digit))

It expands to (rx-compile "<pattern>" "<names>"), with the pattern and the group names worked out at compile time. Its value is an Rx. #rx(seq a b) reads as (#rx seq a b), so a form may be written with or without its own parentheses.

Two spelling rules. Anything that takes no argument is a keyword: :bos, :any, :digit. Anything that takes arguments is a form headed by a symbol: (seq ...), (+ ...), (in ...). A string or a character is itself: a literal sequence in pattern position, the set of its characters inside a set.

PatternMatches
"text", #\cExactly that text or character.
(lit "text")Exactly that text. It takes one string or character.
(seq a b ...)a, then b, and so on.
(or a b ...)a or b or …
(* r), (+ r), (? r)Zero or more, one or more, or zero or one of r.
(*? r), (+? r), (?? r)The same, lazily: as few as will do.
(** least most r)r, from least to most times.
(= n r), (>= n r)r exactly n times, or at least n times.
($ r ...)r, captured in an unnamed group.
(=> :name r ...)r, captured in a group named by the keyword.
:bos, :eosThe beginning and the end of the string.
:anyAny one character, newlines included.
:nonlAny one character but U+000A, the only line break it knows.
a named set, or a set formOne character of the set.

A repetition, $ or => given several patterns takes them in sequence: (+ "a" "b") is (+ (seq "a" "b")). Counts are whole numbers. Groups are numbered from zero in the order they open.

Character sets match one character. They are worked out at compile time, and a set that can match nothing is an error.

SetHolds
#\c, "abc"Those characters.
(in a b ...)Every character in any of the sets; (or a b ...) inside a set is the same.
(/ lo hi ...)The ranges from lo to hi, the endpoints taken in pairs.
(~ a b ...)Every character in none of the sets.
(- a b ...)The characters of a that are in none of the others.
(& a b ...)The characters in all of the sets.

(/ #\a #\z #\0 #\9) and (/ "az09") are the same set, the lowercase letters and the digits: the endpoints are taken in pairs however they are written. A range runs upwards. A set member or a range endpoint is a character in U+0000 to U+FFFF; above that, match a literal instead.

The named sets are keywords. Where two names are given they are the same set.

NameHolds
:alpha, :alphabeticLetters, in all of Unicode.
:digit, :numericEvery Unicode decimal digit, matching char-numeric?.
:alnum, :alphanumericLetters and decimal digits.
:upper, :upper-caseUppercase letters.
:lower, :lower-caseLowercase letters.
:space, :whitespaceWhitespace: U+0009 to U+000D, the space, and Unicode’s others.
:wordLetters, digits, marks and connector punctuation such as _.
:controlU+0000 to U+001F and U+007F to U+009F.
:hex-digit0 to 9, A to F and a to f.
:asciiU+0000 to U+007F.
:ascii-digit0 to 9.
:ascii-alphaA to Z and a to z.
:ascii-alnumThe ASCII letters and digits.
:anyEvery character.
:nonlEvery character but U+000A.

Mind :digit. It holds every Unicode decimal digit, such as ٩ and ०, not only 0 to 9. A pattern for the ASCII digits wants :ascii-digit, and the same goes for :alpha and :alnum.

See also: rx-compile, rx-match, rx-search

Rx

macro ↑ Macros

(Rx regex pattern)

A match pattern: matches a regular expression against the whole string.

regex
The regular expression: an Rx, such as a #rx(...) or a name bound to one.
pattern
Sub-pattern matched against the (Option RxMatch) returned by rx-match.
(match line
  ((Rx phone (Some m)) (str "phone: " (option-ref-or (rx-group m :area) "?")))
  ((RxIn #rx((+ :digit)) (Some m)) (str "a number: " (rx-text m)))
  (_ "neither"))

Rx is also the name of the type of a compiled regular expression, which rx-compile describes.

A view covers nothing for exhaustiveness, so a match using Rx or RxIn still needs a clause that matches anything.

The regular expression is evaluated each time the clause is tried. The compiled regex is cached, but a #rx(...) written in the clause is still looked up anew each time, so bind the pattern once, at the top level, and name it in the clause:

(def digits #rx((+ :digit)))

(match text
  ((RxIn digits (Some m)) (rx-text m))
  (_ "none"))

See also: RxIn, rx-match

RxIn

macro ↑ Macros

(RxIn regex pattern)

A match pattern: searches the string for a regular expression.

regex
The regular expression: an Rx, such as a #rx(...) or a name bound to one.
pattern
Sub-pattern matched against the (Option RxMatch) returned by rx-search.

As Rx, but finding the leftmost match anywhere in the string. A match using it still needs a clause that matches anything.

See also: Rx, rx-search