Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Regular expressions

A regular expression is written between slashes, optionally followed by flags:

/pattern/flags
FlagMeaning
iIgnore case
mMulti-line: ^ and $ match at line breaks

The syntax and semantics are those of JavaScript regular expressions (ECMAScript). They include character classes, quantifiers (greedy and lazy), groups, backreferences, lookahead and lookbehind, and Unicode escapes. jlens uses the regress engine, which follows the ECMAScript specification. An invalid pattern fails with S0302.

Like JavaScript’s, the engine backtracks, so some patterns take exponential time on inputs they almost match: /^(a+)+$/ on thirty a followed by ! tries half a billion ways to split the run. When the evaluation has a timeout or a cancellation token, a match stops with D1012 or U1003 like any other step (ADR 0009). Set a timeout when the expressions come from users.

Functions that take a regular expression

  • $contains tests whether a string matches.
  • $match returns the matches, with positions and groups.
  • $replace replaces matches, with references to groups.
  • $split splits a string around matches.
input: address.json
Email.address[$contains($, /@my-work\./)]
=> ["fred.smith@my-work.com", "fsmith@my-work.com"]
$replace("2024-03-15", /(\d+)-(\d+)-(\d+)/, "$3/$2/$1")
=> "15/03/2024"

Matching in predicates

A regular expression is a function: applied to a string, it returns the first match, or nothing. With the chain operator ~>, that makes a compact test in a filter:

input: invoice.json
Account.Order.Product[`Product Name` ~> /hat/i].`Product Name`
=> ["Bowler Hat", "Trilby hat", "Bowler Hat"]

Matcher functions

Called directly, a regular expression returns an object describing the first match:

  • match: the matched text;
  • start and end: its positions;
  • groups: the captured texts;
  • next: a function that returns the next match, or nothing.
(
  $matcher := /[a-z]*an[a-z]*/i;
  $m := $matcher("A man, a plan, a canal, Panama!");
  [$m.match, $m.start, $m.end, $m.next().match]
)
=> ["man", 2, 5, "plan"]

$contains, $match, $replace and $split only rely on this contract. Any function that returns such objects can therefore stand in for a regular expression, for example a lambda that implements a custom matching rule.

Positions in match objects are character offsets as JavaScript counts them, in UTF-16 code units. A character outside the Basic Multilingual Plane counts as two.


Adapted from the JSONata documentation (MIT license).